From 866a40769ab3c5f2103fceb93f2c1b390e9db979 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 22 Apr 2024 08:58:46 -0400 Subject: [PATCH 001/483] copilot-theorem: Look for `falsifiable` (not `invalid`). Refs #495. In Kind2, disproven properties are tagged with `falsifiable` in the XML output, but the code in `copilot-theorem`'s Kind2 backend was instead searching for a tag named `invalid`. As a result, `copilot-theorem` would error when attempting to disprove properties that are false, as it fail to parse the XML output. This fixes the issue by replacing `invalid` with `falsifiable`. --- copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs index 49da4790f..42a2f60dc 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs @@ -19,9 +19,9 @@ parseOutput :: String -- ^ Property whose validity is being checked. parseOutput prop xml = fromJust $ do root <- parseXMLDoc xml case findAnswer . findPropTag $ root of - "valid" -> return (Output Valid []) - "invalid" -> return (Output Invalid []) - s -> err $ "Unrecognized status : " ++ s + "valid" -> return (Output Valid []) + "falsifiable" -> return (Output Invalid []) + s -> err $ "Unrecognized status : " ++ s where From b153fbc2231ad33862d2b7a94986f0a90bf501f9 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 22 Apr 2024 09:08:11 -0400 Subject: [PATCH 002/483] copilot-theorem: Document changes in CHANGELOG. Refs #495. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index bcaa4842a..1c091e591 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-05 + * Fix handling of unsatisfiable properties with Kind2. (#495) + 2024-03-07 * Version bump (3.19). (#504) From bdf0d6352e8d9fcd009e1e7d578958df7da0ef5e Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Mon, 29 Apr 2024 11:46:38 +0200 Subject: [PATCH 003/483] copilot-theorem: Remove mention of non-existent `Driver.hs` in README. Refs #452. The section about combining provers mentioned this was shown in the `Driver.hs` file, which does not exist anymore. Additionally, the 'Some examples' section mentioned `Driver.hs` as part of the examples, containing a main function. The `Driver.hs` file does not exist anymore, so these references should be removed. --- copilot-theorem/README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 4d75004b3..062a6b08c 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -202,7 +202,7 @@ precise* output. It would be interesting to implement other merging behaviours in the future. For instance, a *lazy* one such that C launches B only if A has returns *unknown* or *error*. -As an example, the following prover is used in `Driver.hs`: +As an example: ```haskell prover = @@ -251,12 +251,7 @@ later. ### Some examples -Some examples are in the *examples* folder. The `Driver.hs` contains the `main` -function to run any example. Each other example file exports a specification -`spec` and a proof scheme `scheme`. You can change the example being run just -by changing one *import* directive in `Driver.hs`. - -These examples include: +Some examples are in the *examples* folder, including: * `Incr.hs` : a straightforward example in the style of the previous one. * `Grey.hs` : an example where two different implementations of a periodical From 78d7fbe3588bf42beae2309b3a8c7f727b19f158 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Mon, 29 Apr 2024 14:16:02 +0200 Subject: [PATCH 004/483] copilot-theorem: Remove sections about `lightProver` in README. Refs #452. The Light Prover implementation is no longer part of copilot-theorem, and has been replaced by several K-Induction implementations. The sections in the README can be removed. --- copilot-theorem/README.md | 134 ++------------------------------------ 1 file changed, 4 insertions(+), 130 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 062a6b08c..e770c8137 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -67,8 +67,8 @@ Let's say we want to check that `gt0` holds. For this, we use the `prove :: Prover -> ProofScheme -> Spec -> IO ()` function exported by `Copilot.Theorem`. This function takes three arguments: -* The prover we want to use. For now, two provers are available, exported by - the `Copilot.Theorem.Light` and `Copilot.Theorem.Kind2` module. +* The prover we want to use. For now, several provers are available, exported by + the `Copilot.Theorem.Provers.SMT` and `Copilot.Theorem.Kind2` modules. * A *proof scheme*, which is a sequence of instructions like *check*, *assume*, *assert*... * The Copilot specification @@ -120,64 +120,6 @@ argument and checks if the assumptions logically entail the conclusion. -Two provers are provided by default: `Light` and `Kind2`. - -#### The light prover - -The *light prover* is a really simple prover which uses the Yices SMT solver -with the `QF_UFLIA` theory and is limited to prove *k-inductive* properties, -that is properties such that there exists some *k* such that: - -* The property holds during the first *k* steps of the algorithm. -* From the hypothesis the property has held during *k* consecutive steps, we - can prove it is still true one step further. - -For instance, in this example - -```haskell -spec :: Spec -spec = do - prop "gt0" (x > 0) - prop "neq0" (x /= 0) - where - x = [1] ++ (1 + x) -``` -the property `"gt0"` is inductive (1-inductive) but the property `"neq0"` is -not. - - -The *light prover* is defined in `Copilot.Theorem.Light`. This module exports the -`lightProver :: Options -> Prover` function which builds a prover from a record -of type `Options` : - -```haskell -data Options = Options - { kTimeout :: Integer - , onlyBmc :: Bool - , debugMode :: Bool } -``` - -Here, - -* `kTimeout` is the maximum number of steps of the k-induction algorithm the - prover executes before giving up. -* If `onlyBmc` is set to `True`, the prover will only search for - counterexamples and won't try to prove the properties discharged to it. -* If `debugMode` is set to `True`, the SMTLib queries produced by the prover - are displayed in the standard output. - -`Options` is an instance of the `Data.Default` typeclass: - -```haskell -instance Default Options where - def = Options - { kTimeout = 100 - , debugMode = False - , onlyBmc = False } -``` - -Therefore, `def` stands for the default configuration. - #### The Kind2 prover The *Kind2* prover uses the model checker with the same name, from Iowa @@ -283,8 +225,8 @@ available: * The **IL** format: a Copilot program is translated into a list of quantifier-free equations over integer sequences, implicitly universally quantified by a free variable *n*. Each sequence roughly corresponds to a - stream. This format is the one used in G. Hagen's thesis [4]. The *light - prover* works with this format. + stream. This format is the one used in G. Hagen's thesis [4]. Several provers + work with this format. * The **TransSys** format: a Copilot program is *flattened* and translated into a *state transition system* [1]. Moreover, in order to keep some @@ -391,66 +333,6 @@ The file `CoreUtils/Operators` contains helper functions to translate Copilot operators into *copilot-theorem* operators. -#### The Light prover - -As said in the tutorial, the *light prover* is a simple tool implementing the -basic *k-induction* algorithm [1]. The `Light` directory contains three files: - -* `Prover.hs`: the prover and the *k-induction* algorithm are implemented in - this file. -* `SMT.hs` contains some functions to interact with the Yices SMT provers. -* `SMTLib.hs` is a set of functions to output SMTLib directives. It uses the - `Misc.SExpr` module to deal with S-expressions. - -The code is both concise and simple and should be worth a look. - -The prover first translates the copilot specification into the *IL* format. -This translation is implemented in `IL.Translate`. It is straightforward as the -*IL* format does not differ a lot from the *copilot core* format. This is the -case because the reification process has transformed the copilot program such -that the `++` operator only occurs at the top of a stream definition. -Therefore, each stream definition directly gives us a recurrence equation and -initial conditions for the associated sequence. - -The translation process mostly: - -* converts the types and operators, using uninterpreted functions to handle - non-linear operators and external functions. -* creates a sequence for each stream, local stream ands external stream. - -The reader is invited to use the *light prover* on the examples with `debugMode -= true`, in order to have a look at the SMTLib code produced. For instance, if -we check the property `"pos"` on the previous example involving the Fibonacci -sequence, we get: - -``` - (set-logic QF_UFLIA) - (declare-fun n () Int) - (declare-fun s0 (Int) Int) - (assert (= (s0 (+ n 2)) (+ (s0 (+ n 0)) (s0 (+ n 1))))) - (assert (= (s0 (+ n 3)) (+ (s0 (+ n 1)) (s0 (+ n 2))))) - (assert (> (s0 (+ n 0)) 0)) - (push 1) - (assert (or false (not (> (s0 (+ n 1)) 0)))) - (check-sat) - (pop 1) - (assert (= (s0 (+ n 4)) (+ (s0 (+ n 2)) (s0 (+ n 3))))) - (assert (> (s0 (+ n 1)) 0)) - (push 1) - (assert (or false (not (> (s0 (+ n 2)) 0)))) - (check-sat) -unsat - (pop 1) -``` - -Here, we just kept the outputs related to the `` psolver, which is the -solver trying to prove the *continuation step*. - -You can see that the SMT solver is used in an incremental way (`push` and `pop` -instructions), so we don't need to restart it at each step of the algorithm -(see [2]). - - #### The Kind2 prover The *Kind2 prover* first translates the copilot specification into a *modular @@ -802,14 +684,6 @@ architecture of Kind2. ## FAQ -### Why does the light prover not deliver counterexamples ? - -The problem is the light prover is using uninterpreted functions to represent -streams and Yices2 can't give you values for uninterpreted functions when you -ask it for a valid assignment. Maybe we could get better performances and -easily counterexample display if we rewrite the *light prover* so that it works -with *transition systems* instead of *IL*. - ### Why does the code related to transition systems look so complex ? It is true the code of `TransSys` is quite complex. In fact, it would be really From beb6e28ed5d237a04cdafdf60d4c2b2d8242af12 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Wed, 1 May 2024 20:00:09 +0200 Subject: [PATCH 005/483] copilot-theorem: Replace usage of Light Prover in README. Refs #452. Parts referring to usage specifics of the Light Prover should be updated to the equivalent K-Induction prover, this commit updates these parts. --- copilot-theorem/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index e770c8137..5986bd9a8 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -76,10 +76,10 @@ This function takes three arguments: Here, we can just write ```haskell -prove (lightProver def) (check "gt0") spec +prove (kInduction def) (check "gt0") spec ``` -where `lightProver def` stands for the *light prover* with default +where `kInduction def` stands for the *K-Induction prover* with default configuration. ### The Prover interface @@ -133,7 +133,7 @@ It is provided by the `Copilot.Theorem.Kind2` module, which exports a `kind2Prov data Options = Options { bmcMax :: Int } ``` and where `bmcMax` corresponds to the `--bmc_max` option of *kind2* and is -equivalent to the `kTimeout` option of the light prover. Its default value is +equivalent to the `maxK` option of the K-Induction prover. Its default value is 0, which stands for infinity. #### Combining provers @@ -148,7 +148,7 @@ As an example: ```haskell prover = - lightProver def {onlyBmc = True, kTimeout = 5} + kInduction def {kTimeout = 5} `combine` kind2Prover def ``` @@ -563,14 +563,14 @@ The use of SMT solvers introduces two kind of limitations: Let's consider the first point. SMT solving is costly and its performances are sometimes unpredictable. For instance, when running the `SerialBoyerMoore` -example with the *light prover*, Yices2 does not terminate. However, the *z3* +example with the *k-induction prover*, Yices2 does not terminate. However, the *z3* SMT solver used by *Kind2* solves the problem instantaneously. Note that this performance gap is not due to the use of the IC3 algorithm because the property to check is inductive. It could be related to the fact the SMT problem produced -by the *light prover* uses uninterpreted functions for encoding streams instead +by the *k-induction prover* uses uninterpreted functions for encoding streams instead of simple integer variables, which is the case when the copilot program is translated into a transition system. However, this wouldn't explain why the -*light prover* still terminates instantaneously on the `BoyerMoore` example, +*k-induction prover* still terminates instantaneously on the `BoyerMoore` example, which seems not simpler by far. The second point keeps you from expressing or proving some properties From 1d616d4cd42e35f272b477a97050cc1d303ac726 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:08:53 +0200 Subject: [PATCH 006/483] copilot-theorem: Ignore output of `prop` in README. Refs #452. The current `prop` returns a reference to the proposition, we have to discard because otherwise the result is not a `Spec`. --- copilot-theorem/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 5986bd9a8..d2fcb8d4c 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -58,7 +58,7 @@ For instance, here is a straightforward specification declaring one property: ```haskell spec :: Spec spec = do - prop "gt0" (x > 0) + void $ prop "gt0" (x > 0) where x = [1] ++ (1 + x) ``` @@ -162,8 +162,8 @@ Let's consider again this example: ```haskell spec :: Spec spec = do - prop "gt0" (x > 0) - prop "neq0" (x /= 0) + void $ prop "gt0" (x > 0) + void $ prop "neq0" (x /= 0) where x = [1] ++ (1 + x) ``` @@ -249,7 +249,7 @@ The following program: ```haskell spec = do - prop "pos" (fib > 0) + void $ prop "pos" (fib > 0) where fib :: Stream Word64 From 0c77e6cbcf0f91dca3f124621942fde3b21408fb Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:44:31 +0200 Subject: [PATCH 007/483] copilot-theorem: Add `forAll` to propositions in README. Refs #452. The interface for `prop` has changed and now expects a `Prop` of some type as an argument. This commit universally quantifies all argument streams in uses of `prop`. --- copilot-theorem/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index d2fcb8d4c..da3a1c8b7 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -58,7 +58,7 @@ For instance, here is a straightforward specification declaring one property: ```haskell spec :: Spec spec = do - void $ prop "gt0" (x > 0) + void $ prop "gt0" (forAll $ x > 0) where x = [1] ++ (1 + x) ``` @@ -162,8 +162,8 @@ Let's consider again this example: ```haskell spec :: Spec spec = do - void $ prop "gt0" (x > 0) - void $ prop "neq0" (x /= 0) + void $ prop "gt0" (forAll $ x > 0) + void $ prop "neq0" (forAll $ x /= 0) where x = [1] ++ (1 + x) ``` @@ -249,7 +249,7 @@ The following program: ```haskell spec = do - void $ prop "pos" (fib > 0) + void $ prop "pos" (forAll $ fib > 0) where fib :: Stream Word64 From 4be277768e77b9d207057bffb2c2f369a8dfb49c Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:44:55 +0200 Subject: [PATCH 008/483] copilot-theorem: Update installation instructions in README. Refs #452. The installation instructions were outdated. This commit points users to the only installation instructions of Copilot. --- copilot-theorem/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index da3a1c8b7..761d3951f 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -28,15 +28,13 @@ properties on Copilot programs. It includes: *copilot-theorem* needs the following dependencies to be installed: -* The *copilot-core* and *copilot-language* Haskell libraries +* The *copilot-core* and *copilot-prettyprinter* Haskell libraries * The *Yices2* SMT-solver: `yices-smt2` must be in your `$PATH` * The *Z3* SMT-solver: `z3` must be in your `$PATH` * The *Kind2* model checker: `kind2` must be in your `$PATH` -To build it, just clone this repository and use `cabal install`. You will find -some examples in the `examples` folder, which can be built with `cabal install` -too, producing an executable `copilot-theorem-example` in your `.cabal/bin` -folder. +To build it, just install the Copilot library as described in the top-level +README. ### First steps From 0fdd6f53878a340de571d9b08781110716525931 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:50:46 +0200 Subject: [PATCH 009/483] copilot-theorem: Update usage of `prove` in README. Refs #452. The interface of `prove` has changed a lot. It no longer matches the description. This commit updates all mentions of `prove`, both text and examples, to match the current interface. The old examples used the `kInduction` prover which no longer matches the interface. This commit replaces those calls by the Kind2 prover. --- copilot-theorem/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 761d3951f..9f1311810 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -62,22 +62,24 @@ spec = do ``` Let's say we want to check that `gt0` holds. For this, we use the `prove :: -Prover -> ProofScheme -> Spec -> IO ()` function exported by `Copilot.Theorem`. +Copilot.Core.Spec.Spec -> String -> UProof -> IO Bool` function exported by +`Copilot.Theorem`. This function takes three arguments: -* The prover we want to use. For now, several provers are available, exported by - the `Copilot.Theorem.Provers.SMT` and `Copilot.Theorem.Kind2` modules. -* A *proof scheme*, which is a sequence of instructions like *check*, *assume*, - *assert*... -* The Copilot specification +* A reified Copilot.Language.Spec. +* The name of the proposition we want to check. +* A strategy to prove the proposition. -Here, we can just write +In this case the proposition is simple enough so that we can check it directly +by k-induction using `kind2Prover`. Therefore we can just write: ```haskell -prove (kInduction def) (check "gt0") spec +main = do + spec' <- reify spec + prove spec' "gt0" (tell [Check $ kind2Prover def]) ``` -where `kInduction def` stands for the *K-Induction prover* with default +where `kind2Prover def` stands for the *Kind2 prover* with default configuration. ### The Prover interface From 383f9bd22d0086eb3ca24729d5af790609abda61 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:52:09 +0200 Subject: [PATCH 010/483] copilot-theorem: Remove outdated section about prover interface in README. Refs #452. The section did not reflect the current state of copilot-theorem anymore. We will make a decision later whether a replacement is necessary, but for now it is simply removed. --- copilot-theorem/README.md | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 9f1311810..710af1eb6 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -82,44 +82,6 @@ main = do where `kind2Prover def` stands for the *Kind2 prover* with default configuration. -### The Prover interface - -The `Copilot.Theorem.Prover` defines a general interface for provers. Therefore, -it is really easy to add a new prover by creating a new object of type -`Prover`. The latter is defined like this: - -```haskell -data Cex = Cex - -type Infos = [String] - -data Output = Output Status Infos - -data Status - = Valid - | Invalid (Maybe Cex) - | Unknown - | Error - -data Feature = GiveCex | HandleAssumptions - -data Prover = forall r . Prover - { proverName :: String - , hasFeature :: Feature -> Bool - , startProver :: Core.Spec -> IO r - , askProver :: r -> [PropId] -> PropId -> IO Output - , closeProver :: r -> IO () - } -``` - -Each prover mostly has to provide a `askProver` function which takes as an -argument -* The prover descriptor -* A list of assumptions -* A conclusion - -and checks if the assumptions logically entail the conclusion. - #### The Kind2 prover The *Kind2* prover uses the model checker with the same name, from Iowa From a5340b453991bdac9f3390c3b9b5024d1bbfd7c5 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 18:53:39 +0200 Subject: [PATCH 011/483] copilot-theorem: Remove section about combining provers in README. Refs #452. Currently only the Kind2 prover can be combined with itself, so it no longer makes sense to have a section about combining provers. This commit removes that section. --- copilot-theorem/README.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 710af1eb6..f47904c34 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -98,25 +98,6 @@ and where `bmcMax` corresponds to the `--bmc_max` option of *kind2* and is equivalent to the `maxK` option of the K-Induction prover. Its default value is 0, which stands for infinity. -#### Combining provers - -The `combine :: Prover -> Prover -> Prover` function lets you merge two provers -A and B into a prover C which launches both A and B and returns the *most -precise* output. It would be interesting to implement other merging behaviours -in the future. For instance, a *lazy* one such that C launches B only if A has -returns *unknown* or *error*. - -As an example: - -```haskell -prover = - kInduction def {kTimeout = 5} - `combine` kind2Prover def -``` - -We will discuss the internals and the experimental results of these provers -later. - ### Proof schemes Let's consider again this example: From 1afdeb93ac44717d61961fabf64959516fc4f765 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 19:00:16 +0200 Subject: [PATCH 012/483] copilot-theorem: Remove outdated section about proof schemes in README. Refs #452. The section did not reflect the current state of copilot-theorem anymore. We will make a decision later whether a replacement is necessary, but for now it is simply removed. --- copilot-theorem/README.md | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index f47904c34..1f1ac3c7d 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -98,42 +98,6 @@ and where `bmcMax` corresponds to the `--bmc_max` option of *kind2* and is equivalent to the `maxK` option of the K-Induction prover. Its default value is 0, which stands for infinity. -### Proof schemes - -Let's consider again this example: - -```haskell -spec :: Spec -spec = do - void $ prop "gt0" (forAll $ x > 0) - void $ prop "neq0" (forAll $ x /= 0) - where - x = [1] ++ (1 + x) -``` - -and let's say we want to prove `"neq0"`. Currently, the two available solvers -fail at showing this non-inductive property (we will discuss this limitation -later). Therefore, we can prove the more general inductive lemma `"gt0"` and -deduce our main goal from this. For this, we use the proof scheme - -```haskell -assert "gt0" >> check "neq0" -``` -instead of just `check "neq0"`. A proof scheme is chain of primitives schemes -glued by the `>>` operator. For now, the available primitives are: - -* `check "prop"` checks whether or not a given property is true in the current - context. -* `assume "prop"` adds an assumption in the current context. -* `assert "prop"` is a shortcut for `check "prop" >> assume "prop"`. -* `assuming :: [PropId] -> ProofScheme -> ProofScheme` is such that `assuming - props scheme` assumes the list of properties *props*, executes the proof - scheme *scheme* in this context, and forgets the assumptions. -* `msg "..."` displays a string in the standard output - -We will discuss the limitations of this tool and a way to use it in practice -later. - ### Some examples Some examples are in the *examples* folder, including: From b76c1ef309dfdcf4d637dee6146aa3c4ba76d767 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 19:07:07 +0200 Subject: [PATCH 013/483] copilot-theorem: Remove references to old modules in README. Refs #452. Remove mentions of modules that no longer exist. --- copilot-theorem/README.md | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 1f1ac3c7d..e16d7f1ea 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -147,9 +147,6 @@ contains at least * `PrettyPrint.hs` for pretty printing (useful for debugging) * `Translate.hs` where the translation process from `Core.Spec` is defined. -These three formats share a simplified set of types and operators, defined -respectively in `Misc.Type` and `Misc.Operator`. - ##### An example The following program: @@ -226,20 +223,6 @@ other Copilot libraries. *copilot-theorem* handles only three types which are sense it ignores integer overflow problems and the loss of precision due to floating point arithmetic. -The rules of translation between Copilot types and *copilot-theorem* types are -defined in `Misc/Cast`. - -#### Operators - -The operators provided by `Misc.Operator` mostly consists in boolean -connectors, linear operators, equality and inequality operators. If other -operators are used in the Copilot program, they are handled using -non-determinism or uninterpreted functions. - -The file `CoreUtils/Operators` contains helper functions to translate Copilot -operators into *copilot-theorem* operators. - - #### The Kind2 prover The *Kind2 prover* first translates the copilot specification into a *modular @@ -493,8 +476,7 @@ arbitrary constant stream because it is the quantified property which is inductive and not the property specialized for a given constant stream. That's why we have no other solution than replacing universal quantification by *bounded* universal quantification by assuming all the elements of the input -stream are in the finite list `allowed` and using the function `forAllCst` -defined in `Copilot.Kind.Lib`: +stream are in the finite list `allowed` and using the function `forAllCst`: ```haskell conj :: [Stream Bool] -> Stream Bool @@ -565,15 +547,6 @@ architecture of Kind2. + Use Template Haskell to declare automatically some observers with the same names used in the original program. -### Refactoring suggestions - -+ Implement a cleaner way to deal with arbitrary streams and arbitrary - constants by extending the `Copilot.Core.Expr type`. See the - `Copilot.Kind.Lib` module to observe how inelegant the current solution is. - -+ Use `Cnub` as an intermediary step in the translation from `Core.Spec` to - `IL.Spec` and `TransSys.Spec`. - ### More advanced enhancements + Enhance the proof scheme system such that when proving a property depending From c947b3ef432a534e304e701ab4ef62a3ebd93a18 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 7 May 2024 19:17:18 +0200 Subject: [PATCH 014/483] copilot-theorem: Document changes in CHANGELOG. Refs #452. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 1c091e591..417a03696 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ -2024-05-05 +2024-05-07 * Fix handling of unsatisfiable properties with Kind2. (#495) + * Remove outdated details from README. (#452) 2024-03-07 * Version bump (3.19). (#504) From 5834d442aeb5c9a3f63484c93026017db3b77520 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 8 May 2024 06:20:47 +0000 Subject: [PATCH 015/483] Bump versions and constraints. Refs #512. --- copilot-c99/copilot-c99.cabal | 8 +++---- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 18 +++++++------- copilot-libraries/copilot-libraries.cabal | 12 +++++----- .../copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 ++--- copilot/copilot.cabal | 24 +++++++++---------- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 70a9ec422..48079d8ba 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 3.19 +version : 3.19.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,9 +45,9 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 3.19 && < 3.20 - , language-c99 >= 0.2.0 && < 0.3 - , language-c99-simple >= 0.3 && < 0.4 + , copilot-core >= 3.19.1 && < 3.20 + , language-c99 >= 0.2.0 && < 0.3 + , language-c99-simple >= 0.3 && < 0.4 exposed-modules : Copilot.Compile.C99 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 4554c52c1..78197bbed 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 3.19 +version: 3.19.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 03fa5405b..c5995cfa1 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 3.19 +version: 3.19.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.19 && < 3.20 + copilot-core >= 3.19.1 && < 3.20 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index b07cdb54e..be1d377e8 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 3.19 +version: 3.19.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -35,16 +35,16 @@ source-repository head library default-language: Haskell2010 hs-source-dirs: src - build-depends: base >= 4.9 && < 5 + build-depends: base >= 4.9 && < 5 - , array >= 0.5 && < 0.6 - , containers >= 0.4 && < 0.7 - , data-reify >= 0.6 && < 0.7 - , mtl >= 2.0 && < 3 + , array >= 0.5 && < 0.6 + , containers >= 0.4 && < 0.7 + , data-reify >= 0.6 && < 0.7 + , mtl >= 2.0 && < 3 - , copilot-core >= 3.19 && < 3.20 - , copilot-interpreter >= 3.19 && < 3.20 - , copilot-theorem >= 3.19 && < 3.20 + , copilot-core >= 3.19.1 && < 3.20 + , copilot-interpreter >= 3.19.1 && < 3.20 + , copilot-theorem >= 3.19.1 && < 3.20 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 6d72ab2f8..e8830fc60 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 3.19 +version: 3.19.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -36,12 +36,12 @@ library hs-source-dirs: src - build-depends: base >= 4.9 && < 5 + build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.7 - , mtl >= 2.0 && < 2.4 - , parsec >= 2.0 && < 3.2 - , copilot-language >= 3.19 && < 3.20 + , containers >= 0.4 && < 0.7 + , mtl >= 2.0 && < 2.4 + , parsec >= 2.0 && < 3.2 + , copilot-language >= 3.19.1 && < 3.20 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 74f421cfc..bcdb3d142 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 3.19 +version: 3.19.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.19 && < 3.20 + copilot-core >= 3.19.1 && < 3.20 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 225ccd456..4616c0810 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 3.19 +version : 3.19.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.6 - , copilot-core >= 3.19 && < 3.20 - , copilot-prettyprinter >= 3.19 && < 3.20 + , copilot-core >= 3.19.1 && < 3.20 + , copilot-prettyprinter >= 3.19.1 && < 3.20 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 3f598386c..7c0969c3b 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 3.19 +version: 3.19.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -47,17 +47,17 @@ library -Wall -fno-warn-orphans build-depends: - base >= 4.9 && < 5 - , optparse-applicative >= 0.14 && < 0.19 - , directory >= 1.3 && < 1.4 - , filepath >= 1.4 && < 1.5 - - , copilot-core >= 3.19 && < 3.20 - , copilot-theorem >= 3.19 && < 3.20 - , copilot-language >= 3.19 && < 3.20 - , copilot-libraries >= 3.19 && < 3.20 - , copilot-c99 >= 3.19 && < 3.20 - , copilot-prettyprinter >= 3.19 && < 3.20 + base >= 4.9 && < 5 + , optparse-applicative >= 0.14 && < 0.19 + , directory >= 1.3 && < 1.4 + , filepath >= 1.4 && < 1.5 + + , copilot-core >= 3.19.1 && < 3.20 + , copilot-theorem >= 3.19.1 && < 3.20 + , copilot-language >= 3.19.1 && < 3.20 + , copilot-libraries >= 3.19.1 && < 3.20 + , copilot-c99 >= 3.19.1 && < 3.20 + , copilot-prettyprinter >= 3.19.1 && < 3.20 exposed-modules: Language.Copilot, Language.Copilot.Main From 3a2be02e232322d339b89a036787791dc3b61e8f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 8 May 2024 06:22:31 +0000 Subject: [PATCH 016/483] Document changes in CHANGELOG. Refs #512. --- copilot-c99/CHANGELOG | 3 +++ copilot-core/CHANGELOG | 3 +++ copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 3 +++ copilot-libraries/CHANGELOG | 3 +++ copilot-prettyprinter/CHANGELOG | 3 +++ copilot-theorem/CHANGELOG | 1 + copilot/CHANGELOG | 3 +++ 8 files changed, 22 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 9e3cd8b43..0c1e0556b 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 761fcebe9..67d9686d5 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) * Remove deprecated functions in Copilot.Core.Type and diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 0a7a64611..259c86cf6 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 8834a2974..f7f1c89cb 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 6a7bb7514..7fa4005fa 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 3d8091be9..e2dd781c9 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 417a03696..348227766 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ 2024-05-07 + * Version bump (3.19.1). (#512) * Fix handling of unsatisfiable properties with Kind2. (#495) * Remove outdated details from README. (#452) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 994e999d1..17563c868 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2024-05-07 + * Version bump (3.19.1). (#512) + 2024-03-07 * Version bump (3.19). (#504) From f1ffc113ed50adaaf4c71711908037ad303a40b3 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Mon, 1 Jul 2024 13:35:21 +0200 Subject: [PATCH 017/483] copilot-theorem: Bump `what4` upper bound to `< 1.7`. Refs #514. what4 has seen a new release 1.6, but copilot-theorem needs versions strictly lower than 1.6. This version is API-compatible for all features used by copilot-theorem, but it includes support for GHC 9.8, which we want to also support. --- copilot-theorem/copilot-theorem.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 4616c0810..be5868e9f 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -61,7 +61,7 @@ library , random >= 1.1 && < 1.3 , transformers >= 0.5 && < 0.7 , xml >= 1.3 && < 1.4 - , what4 >= 1.3 && < 1.6 + , what4 >= 1.3 && < 1.7 , copilot-core >= 3.19.1 && < 3.20 , copilot-prettyprinter >= 3.19.1 && < 3.20 From 4cf2dce291b7e111f76cba26c89a39d1b197f34a Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Sat, 6 Jul 2024 00:55:46 +0200 Subject: [PATCH 018/483] copilot-theorem: Document changes in CHANGELOG. Refs #514. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 348227766..df16eb0f8 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-06 + * What4 upper-bound dependency version bump. (#514) + 2024-05-07 * Version bump (3.19.1). (#512) * Fix handling of unsatisfiable properties with Kind2. (#495) From c603bc8cf3c46b1c9d80cc7e652c82a57174e36f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 01:35:47 +0000 Subject: [PATCH 019/483] copilot-language: Remove deprecated function Copilot.Language.Spec.forall. Refs #518. The function Copilot.Language.Spec.forall not being used by any other part of Copilot. It was replaced by a function with a different name due to the lowercase name not being allowed in newer versions of GHC. The original function was deprecated in Copilot 3.18. As per our internal policy of waiting 3 versions from deprecation until a public interface declaration can be removed, this function can now be removed. This commit removes the function Copilot.Language.Spec.forall from copilot-language (both in the module that defines it and in a module that re-exports it). --- copilot-language/src/Copilot/Language.hs | 2 +- copilot-language/src/Copilot/Language/Spec.hs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/copilot-language/src/Copilot/Language.hs b/copilot-language/src/Copilot/Language.hs index 400cc51c1..674d601f7 100644 --- a/copilot-language/src/Copilot/Language.hs +++ b/copilot-language/src/Copilot/Language.hs @@ -38,7 +38,7 @@ module Copilot.Language , prop , theorem , forAll - , forall, exists + , exists ) where import Data.Int hiding (Int) diff --git a/copilot-language/src/Copilot/Language/Spec.hs b/copilot-language/src/Copilot/Language/Spec.hs index 26bc30aee..b5fc94b61 100644 --- a/copilot-language/src/Copilot/Language/Spec.hs +++ b/copilot-language/src/Copilot/Language/Spec.hs @@ -28,7 +28,7 @@ module Copilot.Language.Spec , prop, properties , theorem, theorems , forAll - , forall, exists + , exists , extractProp , Universal, Existential ) where @@ -165,11 +165,6 @@ data Prop a where forAll :: Stream Bool -> Prop Universal forAll = Forall -{-# DEPRECATED forall "Use forAll instead." #-} --- | Universal quantification of boolean streams over time. -forall :: Stream Bool -> Prop Universal -forall = forAll - -- | Existential quantification of boolean streams over time. exists :: Stream Bool -> Prop Existential exists = Exists From a1090fe1b8021125e46f5365cd0e96750fda3f30 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 03:22:31 +0000 Subject: [PATCH 020/483] copilot: Update README to reflect support for GHC 9.8. Refs #518. A recent update to copilot-language makes all of Copilot compatible with GHC 9.8. This commit updates the README to reflect such compatibility. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 18ab79517..aef1fb26b 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -81,7 +81,7 @@ ghci> ghci> Leaving GHCi. On other Linux distributions or older Debian-based distributions, to use Copilot you must install a Haskell compiler (GHC) and the package manager Cabal. We currently support all versions of GHC from 8.6.5 to modern versions -(9.6 as of this writing). You can install the toolchain using +(9.8 as of this writing). You can install the toolchain using [ghcup](https://www.haskell.org/ghcup/) or, if you are on Debian/Ubuntu, directly with `apt-get`: From 41bb643c7f4f8d6ce278f53e9eabaec6354b573c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 01:38:16 +0000 Subject: [PATCH 021/483] copilot-language: Document changes in CHANGELOG. Refs #518. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index f7f1c89cb..d25c83b59 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-06 + * Remove deprecated function Copilot.Language.Spec.forall. (#518) + 2024-05-07 * Version bump (3.19.1). (#512) From 7c8e7dd1691506ee0cbe6079cfe34b7cbeec9f90 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 03:23:28 +0000 Subject: [PATCH 022/483] copilot: Document changes in CHANGELOG. Refs #518. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 17563c868..a9d94f77e 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-06 + * Update README to reflect support for GHC 9.8. (#518) + 2024-05-07 * Version bump (3.19.1). (#512) From 6c8ae3f541e295338b6bf5d784dfb11a37cbc607 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 30 Jun 2024 11:11:50 +0000 Subject: [PATCH 023/483] copilot-core: Introduce new kind of struct field update operator. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. This commit adds a new Op2 that allows struct fields to be modified. We include additional constraints, Typeable and Show, that are needed for the C99 backend and the interpreter. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- copilot-core/src/Copilot/Core/Operators.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Operators.hs b/copilot-core/src/Copilot/Core/Operators.hs index 6e5459d39..b8f903ae9 100644 --- a/copilot-core/src/Copilot/Core/Operators.hs +++ b/copilot-core/src/Copilot/Core/Operators.hs @@ -13,9 +13,10 @@ module Copilot.Core.Operators where -- External imports -import Data.Bits (Bits) -import Data.Word (Word32) -import GHC.TypeLits (KnownSymbol) +import Data.Bits (Bits) +import Data.Typeable (Typeable) +import Data.Word (Word32) +import GHC.TypeLits (KnownSymbol) -- Internal imports import Copilot.Core.Type (Field (..), Type (..)) @@ -96,6 +97,11 @@ data Op2 a b c where Index :: Type (Array n t) -> Op2 (Array n t) Word32 t -- ^ Array access/projection of an array element. + -- Struct operator. + UpdateField :: (Typeable b, KnownSymbol s, Show b) + => Type a -> Type b -> (a -> Field s b) -> Op2 a b a + -- ^ Update a field of a struct. + -- | Ternary operators. data Op3 a b c d where -- Conditional operator. From c9998e35841e095dbdd9a1a1294be637eb21b966 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 16:09:36 +0000 Subject: [PATCH 024/483] copilot-core: Add new update operation to Struct. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. We'd like to be able to modify a struct by adjusting the values of specific fields. In order to add support to the interpreter for Struct field updates, we need an operation that will perform the modification in the specific struct type itself (the one that is an instance of the Struct class). This commit modifies the Struct class with the new operation. We use Value as an argument, which imposes a Typeable and Show constraint, which were added to the Op2 definition in a prior commit. To make the class backwards compatible, we pre-define the method to an error call that indicates to users that the operation has not been defined for that specific type. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- copilot-core/src/Copilot/Core/Type.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index f32260d5b..5710465f3 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -34,6 +34,7 @@ module Copilot.Core.Type , Struct , fieldName , accessorName + , updateField ) where @@ -59,6 +60,9 @@ class Struct a where -- | Transforms all the struct's fields into a list of values. toValues :: a -> [Value a] + updateField :: a -> Value t -> a + updateField = error "Field updates not supported for this type." + -- | The field of a struct, together with a representation of its type. data Value a = forall s t . (Typeable t, KnownSymbol s, Show t) => Value (Type t) (Field s t) From 17160986cd53f9250e062ccbdc26fec5cb721097 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 16:10:38 +0000 Subject: [PATCH 025/483] copilot-interpreter: Implement struct updates in interpreter. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. We'd like to be able to modify a struct by adjusting the values of specific fields. This commit modifies the interpreter to support the evaluation of update field operators applied to streams of structs. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- .../src/Copilot/Interpret/Eval.hs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/copilot-interpreter/src/Copilot/Interpret/Eval.hs b/copilot-interpreter/src/Copilot/Interpret/Eval.hs index a0083524c..3d8df44a7 100644 --- a/copilot-interpreter/src/Copilot/Interpret/Eval.hs +++ b/copilot-interpreter/src/Copilot/Interpret/Eval.hs @@ -2,10 +2,11 @@ -- | A tagless interpreter for Copilot specifications. -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE Safe #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE ScopedTypeVariables #-} module Copilot.Interpret.Eval ( Env @@ -17,8 +18,9 @@ module Copilot.Interpret.Eval import Copilot.Core (Expr (..), Field (..), Id, Name, Observer (..), Op1 (..), Op2 (..), Op3 (..), Spec, Stream (..), - Trigger (..), Type (..), UExpr (..), arrayElems, - specObservers, specStreams, specTriggers) + Trigger (..), Type (..), UExpr (..), Value (..), + arrayElems, specObservers, specStreams, + specTriggers, updateField) import Copilot.Interpret.Error (badUsage) import Prelude hiding (id) @@ -244,6 +246,12 @@ evalOp2 op = case op of BwShiftR _ _ -> ( \ !a !b -> shiftR a $! fromIntegral b ) Index _ -> \xs n -> (arrayElems xs) !! (fromIntegral n) + UpdateField (Struct _) ty (fieldAccessor :: a -> Field s b) -> + \stream fieldValue -> + let newField :: Field s b + newField = Field fieldValue + in updateField stream (Value ty newField) + -- | Apply a function to two numbers, so long as the second one is -- not zero. -- From d7bc241e862e1afbef9bf989a00573f2e5366e8e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 30 Jun 2024 11:13:45 +0000 Subject: [PATCH 026/483] copilot-c99: Expand expression translation environment to include statements. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. We'd like to be able to modify a struct by adjusting the values of specific fields. The current implementation of the translation of expressions generates variable declarations, but we now need to keep a counter of the new variables that have been created, and additional statements that must be executed after the variables are declared. This commit modifies the function environment to allow for such elements, without adding any specific code that uses those. This change, in isolation, should not modify the extensible behavior of Copilot. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- .../src/Copilot/Compile/C99/CodeGen.hs | 29 ++++++++++++------- copilot-c99/src/Copilot/Compile/C99/Expr.hs | 6 ++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs index ad14f1f01..3f9693438 100644 --- a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs +++ b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs @@ -121,11 +121,13 @@ mkAccessDecln sId ty xs = -- | Write a generator function for a stream. mkGenFun :: String -> Expr a -> Type a -> C.FunDef mkGenFun name expr ty = - C.FunDef static cTy name [] cVars [C.Return $ Just cExpr] + C.FunDef static cTy name [] cVars stmts where - static = Just C.Static - cTy = C.decay $ transType ty - (cExpr, cVars) = runState (transExpr expr) mempty + static = Just C.Static + cTy = C.decay $ transType ty + (cExpr, state') = runState (transExpr expr) (0, mempty, mempty) + (_, cVars, stmts') = state' + stmts = stmts' ++ [C.Return $ Just cExpr] -- | Write a generator function for a stream that returns an array. mkGenFunArray :: String -> String -> Expr a -> Type a -> C.FunDef @@ -139,13 +141,20 @@ mkGenFunArray name nameArg expr ty@(Array _) = outputParam = C.Param cArrayType nameArg cArrayType = transType ty - -- Output value, and any variable declarations needed - (cExpr, varDecls) = runState (transExpr expr) mempty + -- Statements to execute as part of the generator. They include statements + -- related to calculating the values of intermediate expressions, and + -- statements related to copying the final value to the destination array. + stmts = stmts' ++ [ copyStmt ] - -- Copy expression to output argument - stmts = [ C.Expr $ memcpy (C.Ident nameArg) cExpr size ] - size = C.LitInt (fromIntegral $ typeSize ty) - C..* C.SizeOfType (C.TypeName $ tyElemName ty) + -- Output value, variable declarations needed, and other statements that + -- must be executed to calculate intermediate values. + (cExpr, state') = runState (transExpr expr) (0, mempty, mempty) + (_, varDecls, stmts') = state' + + -- Copy expression to output argument. + copyStmt = C.Expr $ memcpy (C.Ident nameArg) cExpr size + size = C.LitInt (fromIntegral $ typeSize ty) + C..* C.SizeOfType (C.TypeName $ tyElemName ty) mkGenFunArray _name _nameArg _expr _ty = impossible "mkGenFunArray" "copilot-c99" diff --git a/copilot-c99/src/Copilot/Compile/C99/Expr.hs b/copilot-c99/src/Copilot/Compile/C99/Expr.hs index 1c8108996..aa035f303 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Expr.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Expr.hs @@ -32,7 +32,9 @@ transExpr (Local ty1 _ name e1 e2) = do initExpr = Just $ C.InitExpr e1' -- Add new decl to the tail of the fun env - modify (++ [C.VarDecln Nothing cTy1 name initExpr]) + modify (\(i, x, y) + -> (i, x ++ [C.VarDecln Nothing cTy1 name initExpr], y) + ) transExpr e2 @@ -368,7 +370,7 @@ typeIsFloating _ = False -- | Auxiliary type used to collect all the declarations of all the variables -- used in a function to be generated, since variable declarations are always -- listed first at the top of the function body. -type FunEnv = [C.Decln] +type FunEnv = (Int, [C.Decln], [C.Stmt]) -- | Define a C expression that calls a function with arguments. funCall :: C.Ident -- ^ Function name From def91c5e57a7711577464263942c39e79be8147b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 30 Jun 2024 11:16:12 +0000 Subject: [PATCH 027/483] copilot-c99: Handle new struct update operator. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. We'd like to be able to modify a struct by adjusting the values of specific fields. This commit extends the C99 backend to support updates to fields of structs. We use a dedicated translation that injects statements as well as new variable declarations, which are needed to calculate intermediate values and the returned struct value in generators. The original function that translated Copilot Op2's to C functions is modified to not accept an UpdateField argument, as that case is now handled separately due to its more complex functionality and type. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- copilot-c99/src/Copilot/Compile/C99/Expr.hs | 114 +++++++++++++++----- 1 file changed, 87 insertions(+), 27 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Expr.hs b/copilot-c99/src/Copilot/Compile/C99/Expr.hs index aa035f303..40b130086 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Expr.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Expr.hs @@ -8,19 +8,20 @@ module Copilot.Compile.C99.Expr where -- External imports -import Control.Monad.State ( State, modify ) +import Control.Monad.State ( State, get, modify ) import qualified Data.List.NonEmpty as NonEmpty import qualified Language.C99.Simple as C -- Internal imports: Copilot import Copilot.Core ( Expr (..), Field (..), Op1 (..), Op2 (..), Op3 (..), Type (..), Value (..), accessorName, arrayElems, - toValues ) + toValues, typeSize ) -- Internal imports import Copilot.Compile.C99.Error ( impossible ) import Copilot.Compile.C99.Name ( exCpyName, streamAccessorName ) -import Copilot.Compile.C99.Type ( transLocalVarDeclType, transTypeName ) +import Copilot.Compile.C99.Type ( transLocalVarDeclType, transType, + transTypeName ) -- | Translates a Copilot Core expression into a C99 expression. transExpr :: Expr a -> State FunEnv C.Expr @@ -53,6 +54,48 @@ transExpr (Op1 op e) = do e' <- transExpr e return $ transOp1 op e' +transExpr (Op2 (UpdateField ty1@(Struct _) ty2 f) e1 e2) = do + -- Translating a struct update Op requires initializing a variable to the + -- "old" value, updating the field, and returning the new value. + e1' <- transExpr e1 + e2' <- transExpr e2 + + -- Variable to hold the updated struct + (i, _, _) <- get + let varName = "_v" ++ show i + modify (\(i, x, y) -> (i + 1, x, y)) + + -- Add new var decl + let initDecl = C.VarDecln Nothing cTy1 varName Nothing + cTy1 = transLocalVarDeclType ty1 + modify (\(i, x, y) -> (i, x ++ [initDecl], y)) + + -- Initialize the var to the same value as the original struct + let initStmt = C.Expr + $ C.AssignOp + C.Assign + (C.Ident varName) + e1' + + -- Update field f with given value e2. + let updateStmt = case ty2 of + Array _ -> C.Expr $ memcpy dest e2' size + where + dest = C.Dot (C.Ident varName) (accessorName f) + size = C.LitInt + (fromIntegral $ typeSize ty2) + C..* C.SizeOfType (C.TypeName (tyElemName ty2)) + + _ -> C.Expr + $ C.AssignOp + C.Assign + (C.Dot (C.Ident varName) (accessorName f)) + e2' + + modify (\(i, x, y) -> (i, x, y ++ [ initStmt, updateStmt ])) + + return $ C.Ident varName + transExpr (Op2 op e1 e2) = do e1' <- transExpr e1 e2' <- transExpr e2 @@ -102,32 +145,35 @@ transOp1 op e = -- | Translates a Copilot binary operator and its arguments into a C99 -- expression. +-- +-- PRE: op is not a struct update operation (i.e., 'UpdateField'). transOp2 :: Op2 a b c -> C.Expr -> C.Expr -> C.Expr transOp2 op e1 e2 = case op of - And -> e1 C..&& e2 - Or -> e1 C..|| e2 - Add _ -> e1 C..+ e2 - Sub _ -> e1 C..- e2 - Mul _ -> e1 C..* e2 - Mod _ -> e1 C..% e2 - Div _ -> e1 C../ e2 - Fdiv _ -> e1 C../ e2 - Pow ty -> funCall (specializeMathFunName ty "pow") [e1, e2] - Logb ty -> funCall (specializeMathFunName ty "log") [e2] C../ - funCall (specializeMathFunName ty "log") [e1] - Atan2 ty -> funCall (specializeMathFunName ty "atan2") [e1, e2] - Eq _ -> e1 C..== e2 - Ne _ -> e1 C..!= e2 - Le _ -> e1 C..<= e2 - Ge _ -> e1 C..>= e2 - Lt _ -> e1 C..< e2 - Gt _ -> e1 C..> e2 - BwAnd _ -> e1 C..& e2 - BwOr _ -> e1 C..| e2 - BwXor _ -> e1 C..^ e2 - BwShiftL _ _ -> e1 C..<< e2 - BwShiftR _ _ -> e1 C..>> e2 - Index _ -> C.Index e1 e2 + And -> e1 C..&& e2 + Or -> e1 C..|| e2 + Add _ -> e1 C..+ e2 + Sub _ -> e1 C..- e2 + Mul _ -> e1 C..* e2 + Mod _ -> e1 C..% e2 + Div _ -> e1 C../ e2 + Fdiv _ -> e1 C../ e2 + Pow ty -> funCall (specializeMathFunName ty "pow") [e1, e2] + Logb ty -> funCall (specializeMathFunName ty "log") [e2] C../ + funCall (specializeMathFunName ty "log") [e1] + Atan2 ty -> funCall (specializeMathFunName ty "atan2") [e1, e2] + Eq _ -> e1 C..== e2 + Ne _ -> e1 C..!= e2 + Le _ -> e1 C..<= e2 + Ge _ -> e1 C..>= e2 + Lt _ -> e1 C..< e2 + Gt _ -> e1 C..> e2 + BwAnd _ -> e1 C..& e2 + BwOr _ -> e1 C..| e2 + BwXor _ -> e1 C..^ e2 + BwShiftL _ _ -> e1 C..<< e2 + BwShiftR _ _ -> e1 C..>> e2 + Index _ -> C.Index e1 e2 + UpdateField _ _ _ -> impossible "transOp2" "copilot-c99" -- | Translates a Copilot ternary operator and its arguments into a C99 -- expression. @@ -377,3 +423,17 @@ funCall :: C.Ident -- ^ Function name -> [C.Expr] -- ^ Arguments -> C.Expr funCall name = C.Funcall (C.Ident name) + +-- Write a call to the memcpy function. +memcpy :: C.Expr -> C.Expr -> C.Expr -> C.Expr +memcpy dest src size = C.Funcall (C.Ident "memcpy") [dest, src, size] + +-- Translate a Copilot type to a C99 type, handling arrays especially. +-- +-- If the given type is an array (including multi-dimensional arrays), the +-- type is that of the elements in the array. Otherwise, it is just the +-- equivalent representation of the given type in C. +tyElemName :: Type a -> C.Type +tyElemName ty = case ty of + Array ty' -> tyElemName ty' + _ -> transType ty From 05d616c6d65d7e228dee756dc6a53390e136314b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 30 Jun 2024 11:16:37 +0000 Subject: [PATCH 028/483] copilot-language: High-level array update operator. Refs #520. Copilot currently supports reading values from fields of structs, but not changing them. We'd like to be able to modify a struct by adjusting the values of specific fields. A prior commit has extended copilot-core with a low-level interface to support struct field updates, and the C99 backend and the interpreter have been extended accordingly. This commit extends the high-level language definition to support struct field updates. We need to add an auxiliary type Projection to provide the notation we would like to support. Co-authored-by: Frank Dedden Co-authored-by: Ryan Scott --- .../src/Copilot/Language/Operators/Struct.hs | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/copilot-language/src/Copilot/Language/Operators/Struct.hs b/copilot-language/src/Copilot/Language/Operators/Struct.hs index 78f4d037d..355da508f 100644 --- a/copilot-language/src/Copilot/Language/Operators/Struct.hs +++ b/copilot-language/src/Copilot/Language/Operators/Struct.hs @@ -1,8 +1,35 @@ {-# LANGUAGE Safe #-} -- | Combinators to deal with streams carrying structs. +-- +-- We support two kinds of operations on structs: reading the fields of structs +-- and modifying the fields of structs. +-- +-- To obtain the values of field @x@ of a struct @s@, you can just write: +-- +-- @ +-- expr = s # x +-- @ +-- +-- If you want to update it, use instead a double hash to refer to the field. +-- You can either update the field: +-- +-- @ +-- expr = s ## x =: 75 +-- @ +-- +-- To update it by applying a function to it, for example, the function that +-- updates a stream by one unit, just do: +-- +-- @ +-- expr = s ## x =$ (+1) +-- @ module Copilot.Language.Operators.Struct ( (#) + , Projection + , (##) + , (=:) + , (=$) ) where import Copilot.Core.Type @@ -18,6 +45,46 @@ import GHC.TypeLits (KnownSymbol) -- of type @Word8@, and @s@ is a stream of type @Stream T@, then @s # t2@ has -- type @Stream Word8@ and contains the values of the @t2@ field of the structs -- in @s@ at any point in time. -(#) :: (KnownSymbol s, Typed t, Typed a, Struct a) - => Stream a -> (a -> Field s t) -> Stream t +(#) :: (KnownSymbol f, Typed t, Typed s, Struct s) + => Stream s -> (s -> Field f t) -> Stream t (#) s f = Op1 (GetField typeOf typeOf f) s + +-- | Type represented an unapplied struct field accessor and a stream carrying +-- structs. +data Projection s f t = Projection (Stream s) (s -> Field f t) + +-- | Pair a stream with a field accessor, without applying it to obtain the +-- value of the field. +-- +-- This function is needed to refer to a field accessor when the goal is to +-- update the field value, not just to read it. +(##) :: (KnownSymbol f, Typed t, Typed s, Struct s) + => Stream s -> (s -> Field f t) -> Projection s f t +(##) = Projection + +-- | Create a stream where the field of a struct has been updated with values +-- from another stream. +-- +-- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ +-- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and $sT1$ +-- is a stream of type @Int32@ then @s ## t2 =: sT1@ has type @Stream T@ and +-- contains structs where the value of @t1@ is that of @sT1@ and the value of +-- @t2@ is the value that the same field had in @s@, at any point in time. +infixl 8 =: +(=:) :: (KnownSymbol f, Typed t, Typed s, Struct s) + => Projection s f t -> Stream t -> Stream s +(=:) (Projection s f) v = Op2 (UpdateField typeOf typeOf f) s v + +-- | Create a stream where the field of a struct has been updated by applying a +-- function to it. +-- +-- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ +-- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and $f$ is +-- a function from @Stream Int32 -> Stream Int32@ then @s ## t2 =$ f@ has type +-- @Stream T@ and contains structs where the value of @t1@ is that of @f@ +-- applied to the original value of @t1@ in @s@, and the value of @t2@ is the +-- value that the same field had in @s@, at any point in time. +infixl 8 =$ +(=$) :: (KnownSymbol f, Typed t, Typed s, Struct s) + => Projection s f t -> (Stream t -> Stream t) -> Stream s +(=$) (Projection s f) op = s ## f =: (op (s # f)) From 51996b76006fda289a1b836f23800001c7276793 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 17:26:08 +0000 Subject: [PATCH 029/483] copilot-core: Document changes in CHANGELOG. Refs #520. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 67d9686d5..9d40a3660 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-07 + * Update Op2, Struct to support struct field updates. (#520) + 2024-05-07 * Version bump (3.19.1). (#512) From 1a2bfedb0bc1fc438120927382eb01ffac52364e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 17:31:27 +0000 Subject: [PATCH 030/483] copilot-interpreter: Document changes in CHANGELOG. Refs #520. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 259c86cf6..d69607dbc 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-07 + * Add support for struct field updates. (#520) + 2024-05-07 * Version bump (3.19.1). (#512) From ce34955c0fe734038ff8205402de4d514aee098d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 17:31:42 +0000 Subject: [PATCH 031/483] copilot-c99: Document changes in CHANGELOG. Refs #520. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 0c1e0556b..1114bdef9 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-07 + * Add support for struct field updates. (#520) + 2024-05-07 * Version bump (3.19.1). (#512) From 5fb6d605b4e25714f54a7ebfac1c6afaeb2e7519 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Jul 2024 17:32:17 +0000 Subject: [PATCH 032/483] copilot-language: Document changes in CHANGELOG. Refs #520. --- copilot-language/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index d25c83b59..917338ef5 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,5 +1,6 @@ -2024-07-06 +2024-07-07 * Remove deprecated function Copilot.Language.Spec.forall. (#518) + * Add support for struct field updates. (#520) 2024-05-07 * Version bump (3.19.1). (#512) From 2fcf3a793dceed1692943884921edf8b025343f0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 8 Jul 2024 02:10:15 +0000 Subject: [PATCH 033/483] Bump versions and constraints. Refs #522. --- copilot-c99/copilot-c99.cabal | 8 +++---- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 18 +++++++------- copilot-libraries/copilot-libraries.cabal | 12 +++++----- .../copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 ++--- copilot/copilot.cabal | 24 +++++++++---------- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 48079d8ba..ca3cf1d15 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 3.19.1 +version : 3.20 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,9 +45,9 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 3.19.1 && < 3.20 - , language-c99 >= 0.2.0 && < 0.3 - , language-c99-simple >= 0.3 && < 0.4 + , copilot-core >= 3.20 && < 3.21 + , language-c99 >= 0.2.0 && < 0.3 + , language-c99-simple >= 0.3 && < 0.4 exposed-modules : Copilot.Compile.C99 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 78197bbed..b49a0629a 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 3.19.1 +version: 3.20 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index c5995cfa1..1f844fcfa 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 3.19.1 +version: 3.20 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.19.1 && < 3.20 + copilot-core >= 3.20 && < 3.21 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index be1d377e8..99c5867f2 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 3.19.1 +version: 3.20 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -35,16 +35,16 @@ source-repository head library default-language: Haskell2010 hs-source-dirs: src - build-depends: base >= 4.9 && < 5 + build-depends: base >= 4.9 && < 5 - , array >= 0.5 && < 0.6 - , containers >= 0.4 && < 0.7 - , data-reify >= 0.6 && < 0.7 - , mtl >= 2.0 && < 3 + , array >= 0.5 && < 0.6 + , containers >= 0.4 && < 0.7 + , data-reify >= 0.6 && < 0.7 + , mtl >= 2.0 && < 3 - , copilot-core >= 3.19.1 && < 3.20 - , copilot-interpreter >= 3.19.1 && < 3.20 - , copilot-theorem >= 3.19.1 && < 3.20 + , copilot-core >= 3.20 && < 3.21 + , copilot-interpreter >= 3.20 && < 3.21 + , copilot-theorem >= 3.20 && < 3.21 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index e8830fc60..ad89218d1 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 3.19.1 +version: 3.20 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -36,12 +36,12 @@ library hs-source-dirs: src - build-depends: base >= 4.9 && < 5 + build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.7 - , mtl >= 2.0 && < 2.4 - , parsec >= 2.0 && < 3.2 - , copilot-language >= 3.19.1 && < 3.20 + , containers >= 0.4 && < 0.7 + , mtl >= 2.0 && < 2.4 + , parsec >= 2.0 && < 3.2 + , copilot-language >= 3.20 && < 3.21 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index bcdb3d142..0f452c68d 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 3.19.1 +version: 3.20 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.19.1 && < 3.20 + copilot-core >= 3.20 && < 3.21 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index be5868e9f..496ef8c32 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 3.19.1 +version : 3.20 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 3.19.1 && < 3.20 - , copilot-prettyprinter >= 3.19.1 && < 3.20 + , copilot-core >= 3.20 && < 3.21 + , copilot-prettyprinter >= 3.20 && < 3.21 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 7c0969c3b..cfc61c628 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 3.19.1 +version: 3.20 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -47,17 +47,17 @@ library -Wall -fno-warn-orphans build-depends: - base >= 4.9 && < 5 - , optparse-applicative >= 0.14 && < 0.19 - , directory >= 1.3 && < 1.4 - , filepath >= 1.4 && < 1.5 - - , copilot-core >= 3.19.1 && < 3.20 - , copilot-theorem >= 3.19.1 && < 3.20 - , copilot-language >= 3.19.1 && < 3.20 - , copilot-libraries >= 3.19.1 && < 3.20 - , copilot-c99 >= 3.19.1 && < 3.20 - , copilot-prettyprinter >= 3.19.1 && < 3.20 + base >= 4.9 && < 5 + , optparse-applicative >= 0.14 && < 0.19 + , directory >= 1.3 && < 1.4 + , filepath >= 1.4 && < 1.5 + + , copilot-core >= 3.20 && < 3.21 + , copilot-theorem >= 3.20 && < 3.21 + , copilot-language >= 3.20 && < 3.21 + , copilot-libraries >= 3.20 && < 3.21 + , copilot-c99 >= 3.20 && < 3.21 + , copilot-prettyprinter >= 3.20 && < 3.21 exposed-modules: Language.Copilot, Language.Copilot.Main From 76bbefeeceba599803e67b4b1f226feed87e6d50 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 8 Jul 2024 02:12:08 +0000 Subject: [PATCH 034/483] Document changes in CHANGELOG. Refs #522. --- copilot-c99/CHANGELOG | 1 + copilot-core/CHANGELOG | 1 + copilot-interpreter/CHANGELOG | 1 + copilot-language/CHANGELOG | 1 + copilot-libraries/CHANGELOG | 3 +++ copilot-prettyprinter/CHANGELOG | 3 +++ copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 14 insertions(+), 2 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 1114bdef9..836ff3415 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,4 +1,5 @@ 2024-07-07 + * Version bump (3.20). (#522) * Add support for struct field updates. (#520) 2024-05-07 diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 9d40a3660..b912c7e51 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ 2024-07-07 + * Version bump (3.20). (#522) * Update Op2, Struct to support struct field updates. (#520) 2024-05-07 diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index d69607dbc..e3bfe6d92 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,4 +1,5 @@ 2024-07-07 + * Version bump (3.20). (#522) * Add support for struct field updates. (#520) 2024-05-07 diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 917338ef5..bc428fddc 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ 2024-07-07 + * Version bump (3.20). (#522) * Remove deprecated function Copilot.Language.Spec.forall. (#518) * Add support for struct field updates. (#520) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 7fa4005fa..d93218fa5 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-07 + * Version bump (3.20). (#522) + 2024-05-07 * Version bump (3.19.1). (#512) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index e2dd781c9..8bc83df62 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2024-07-07 + * Version bump (3.20). (#522) + 2024-05-07 * Version bump (3.19.1). (#512) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index df16eb0f8..120e1e677 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2024-07-06 +2024-07-07 + * Version bump (3.20). (#522) * What4 upper-bound dependency version bump. (#514) 2024-05-07 diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index a9d94f77e..a83f6135f 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2024-07-06 +2024-07-07 + * Version bump (3.20). (#522) * Update README to reflect support for GHC 9.8. (#518) 2024-05-07 From d939de564d514b21f237c50be1e1fdcc220437bd Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 8 Jul 2024 08:15:31 -0400 Subject: [PATCH 035/483] copilot-theorem: Support translating `UpdateField` to what4. Refs #524. A new kind of `Op2`, called `UpdateField`, was added to `copilot-core` in a prior commit to support defining streams with updates to struct fields. This commit extends `copilot-theorem` to support reasoning about streams involving struct updates. We do so by providing a new case for the operation that translates a `copilot-core` `UpdateField` into a What4 representations of a Copilot expression. The implementation proves relatively straightforward, as struct field accessors always correspond to a concrete index value, which can then be used to update the value at the corresponding index in the list of struct fields. --- .../src/Copilot/Theorem/What4/Translate.hs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs index f55e3884e..5b84b021b 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs @@ -799,6 +799,8 @@ translateOp2 sym origExpr op xe1 xe2 = case (op, xe1, xe2) of (CE.BwShiftL _ _, xe1, xe2) -> translateBwShiftL xe1 xe2 (CE.BwShiftR _ _, xe1, xe2) -> translateBwShiftR xe1 xe2 (CE.Index _, xe1, xe2) -> translateIndex xe1 xe2 + (CE.UpdateField atp _ftp extractor, structXe, fieldXe) -> + translateUpdateField atp extractor structXe fieldXe where -- Translate an 'CE.Add' operation and its arguments into a what4 -- representation of the appropriate type. @@ -964,6 +966,55 @@ translateOp2 sym origExpr op xe1 xe2 = case (op, xe1, xe2) of liftIO $ buildIndexExpr sym ix xes _ -> unexpectedValues "index operation" + -- Translate an 'CE.UpdateField' operation and its arguments into a what4 + -- representation. This function will panic if one of the following does not + -- hold: + -- + -- - The argument is not a struct. + -- + -- - The struct's field cannot be found. + translateUpdateField :: forall struct s. + KnownSymbol s + => CT.Type struct + -- ^ The type of the struct argument + -> (struct -> CT.Field s b) + -- ^ Extract a struct field + -> XExpr sym + -- ^ The first argument value (should be a struct) + -> XExpr sym + -- ^ The second argument value (should be the same type + -- as the struct field) + -> TransM sym (XExpr sym) + -- ^ The first argument value, but with an updated + -- value for the supplied field. + translateUpdateField structTp extractor structXe newFieldXe = + case (structTp, structXe) of + (CT.Struct s, XStruct structFieldXes) -> + case mIx s of + Just ix -> return $ XStruct $ updateAt ix newFieldXe structFieldXes + Nothing -> + panic [ "Could not find field " ++ show fieldNameRepr + , show s + ] + _ -> unexpectedValues "update-field operation" + where + -- Update an element of a list at a particular index. This assumes the + -- preconditions that the index is a non-negative number that is less + -- than the length of the list. + updateAt :: forall a. Int -> a -> [a] -> [a] + updateAt _ _ [] = [] + updateAt 0 new (_:xs) = new : xs + updateAt n new (x:xs) = x : updateAt (n-1) new xs + + fieldNameRepr :: SymbolRepr s + fieldNameRepr = fieldName (extractor undefined) + + structFieldNameReprs :: CT.Struct struct => struct -> [Some SymbolRepr] + structFieldNameReprs s = valueName <$> CT.toValues s + + mIx :: CT.Struct struct => struct -> Maybe Int + mIx s = elemIndex (Some fieldNameRepr) (structFieldNameReprs s) + -- Check the types of the arguments. If the arguments are bitvector values, -- apply the 'BVOp2'. If the arguments are floating-point values, apply the -- 'FPOp2'. Otherwise, 'panic'. From 634b928862bcbc4db744ef9d2aa82e18781ea28d Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 21 Aug 2024 11:22:01 -0400 Subject: [PATCH 036/483] copilot: Update example to demonstrate struct update support. Refs #524. This commit updates the `what4-structs` example in `copilot` (which depends on `copilot-theorem`) in order to demonstrate that `copilot-theorem` supports reasoning about a basic theorem involving a struct field update. --- copilot/examples/what4/Structs.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/copilot/examples/what4/Structs.hs b/copilot/examples/what4/Structs.hs index de4e4efbd..fc61feadf 100644 --- a/copilot/examples/what4/Structs.hs +++ b/copilot/examples/what4/Structs.hs @@ -64,6 +64,10 @@ spec = do void $ prop "Example 2" $ forAll $ (((battery#other) .!! 2) .!! 3) == (((battery#other) .!! 2) .!! 4) + -- Update a struct field, then check it for equality. + void $ prop "Example 3" $ forAll $ + ((battery ## temp =$ (+1))#temp == (battery#temp + 1)) + main :: IO () main = do spec' <- reify spec From 0ee80ccd9ce6a4856a4f551f590034fbc0c8def2 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 21 Aug 2024 11:51:17 -0400 Subject: [PATCH 037/483] copilot-theorem: Add a test case for struct updates. Refs #524. This adds a basic test case that ensures that applying a struct update followed by accessing the updated field is equivalent to accessing the field and then updating it. --- .../tests/Test/Copilot/Theorem/What4.hs | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs index 8297ba262..dbd98eb4a 100644 --- a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs +++ b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} -- The following warning is disabled due to a necessary instance of SatResult -- defined in this module. {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -6,17 +7,22 @@ module Test.Copilot.Theorem.What4 where -- External imports import Data.Int (Int8) +import Data.Word (Word32) import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) -import Test.QuickCheck (Property, arbitrary, forAll) +import Test.QuickCheck (Arbitrary (arbitrary), Property, + arbitrary, forAll) import Test.QuickCheck.Monadic (monadicIO, run) -- External imports: Copilot -import Copilot.Core.Expr (Expr (Const, Op2)) -import Copilot.Core.Operators (Op2 (..)) +import Copilot.Core.Expr (Expr (Const, Op1, Op2)) +import Copilot.Core.Operators (Op1 (..), Op2 (..)) import Copilot.Core.Spec (Spec (..)) import qualified Copilot.Core.Spec as Copilot -import Copilot.Core.Type (Typed (typeOf)) +import Copilot.Core.Type (Field (..), + Struct (toValues, typeName), + Type (Struct), Typed (typeOf), + Value (..)) -- Internal imports: Modules being tested import Copilot.Theorem.What4 (SatResult (..), Solver (..), prove) @@ -30,6 +36,7 @@ tests = [ testProperty "Prove via Z3 that true is valid" testProveZ3True , testProperty "Prove via Z3 that false is invalid" testProveZ3False , testProperty "Prove via Z3 that x == x is valid" testProveZ3EqConst + , testProperty "Prove via Z3 that a struct update is valid" testProveZ3StructUpdate ] -- * Individual tests @@ -77,6 +84,54 @@ testProveZ3EqConst = forAll arbitrary $ \x -> spec x = propSpec propName $ Op2 (Eq typeOf) (Const typeOf x) (Const typeOf x) +-- | Test that Z3 is able to prove the following expresion valid: +-- @ +-- for all (s :: MyStruct), +-- ((s ## testField =$ (+1)) # testField) == ((s # testField) + 1) +-- @ +testProveZ3StructUpdate :: Property +testProveZ3StructUpdate = forAll arbitrary $ \x -> + monadicIO $ run $ checkResult Z3 propName (spec x) Valid + where + propName :: String + propName = "prop" + + spec :: TestStruct -> Spec + spec s = propSpec propName $ + Op2 + (Eq typeOf) + (getField + (Op2 + (UpdateField typeOf typeOf testField) + sExpr + (add1 (getField sExpr)))) + (add1 (getField sExpr)) + where + sExpr :: Expr TestStruct + sExpr = Const typeOf s + + getField :: Expr TestStruct -> Expr Word32 + getField = Op1 (GetField typeOf typeOf testField) + + add1 :: Expr Word32 -> Expr Word32 + add1 x = Op2 (Add typeOf) x (Const typeOf 1) + +-- | A simple data type with a 'Struct' instance and a 'Field'. This is only +-- used as part of 'testProveZ3StructUpdate'. +newtype TestStruct = TestStruct { testField :: Field "testField" Word32 } + +instance Arbitrary TestStruct where + arbitrary = do + w32 <- arbitrary + return (TestStruct (Field w32)) + +instance Struct TestStruct where + typeName _ = "testStruct" + toValues s = [Value typeOf (testField s)] + +instance Typed TestStruct where + typeOf = Struct (TestStruct (Field 0)) + -- | Check that the solver's satisfiability result for the given property in -- the given spec matches the expectation. checkResult :: Solver -> String -> Spec -> SatResult -> IO Bool From 1818c721a8fea0e67b19954a90b21c4966d0adc9 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 21 Aug 2024 11:53:10 -0400 Subject: [PATCH 038/483] copilot-theorem: Document changes in CHANGELOG. Refs #524. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 120e1e677..efc0d0bcf 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2024-08-30 + * Add support for struct updates in Copilot.Theorem.What4. (#524) + 2024-07-07 * Version bump (3.20). (#522) * What4 upper-bound dependency version bump. (#514) From bf0f4548f07484d8a262eba1784036ff17e5e3aa Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 30 Aug 2024 05:53:33 -0400 Subject: [PATCH 039/483] copilot: Document changes in CHANGELOG. Refs #524. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index a83f6135f..c71906891 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2024-08-30 + * Update example to demonstrate struct update support. (#524) + 2024-07-07 * Version bump (3.20). (#522) * Update README to reflect support for GHC 9.8. (#518) From fc261ea82e185f4caaddd1c9cb25c4ae80bb9c4e Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 8 Jul 2024 08:35:59 -0400 Subject: [PATCH 040/483] copilot-prettyprinter: Pretty-print struct update expressions. Refs #526. This commit adds pretty-printing of expressions that update fields of a struct. --- copilot-prettyprinter/src/Copilot/PrettyPrint.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs index 887346827..a92c61668 100644 --- a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs +++ b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs @@ -100,6 +100,9 @@ ppOp2 op = case op of BwShiftL _ _ -> ppInfix "<<" BwShiftR _ _ -> ppInfix ">>" Index _ -> ppInfix ".!!" + UpdateField (Struct _) _ f -> \ doc1 doc2 -> + parens $ doc1 <+> text "##" <+> text (accessorName f) <+> text "=:" <+> doc2 + UpdateField _ _ _ -> impossible "ppOp2" "Copilot.PrettyPrint" -- | Pretty-print a ternary operation. ppOp3 :: Op3 a b c d -> Doc -> Doc -> Doc -> Doc From cc9ca2276e86676ef41393412224cff96c952a61 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 21 Aug 2024 12:03:35 -0400 Subject: [PATCH 041/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #526. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 8bc83df62..d91bb375e 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2024-08-30 + * Add support for pretty-printing struct update expressions. (#526) + 2024-07-07 * Version bump (3.20). (#522) From 25f1c03e442b71982b2a92fe0a07acf6ed5199ae Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 6 Sep 2024 12:28:31 +0000 Subject: [PATCH 042/483] copilot-language: Rename operator for uniformity. Refs #36. We want to add support to be able to modify values of elements in arrays. In order to provide a uniform interface for arrays and structs, we have decided to unify the syntax for array and struct accessors, so that one operator symbol indicates read access, and doubling the same operator symbol indicates modification access. For example, (!) would indicate accessing an element of an array to consult it, and (!!) would indicate the same but to update it or modify it. The analogous operators for structs would be (#) and (##). At present, the symbol being used to access elements of arrays (.!!), which is not consistent with the naming approach described above. This commit defines a new operator (!) for array element access, and deprecates the old (.!!). --- .../src/Copilot/Language/Operators/Array.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/copilot-language/src/Copilot/Language/Operators/Array.hs b/copilot-language/src/Copilot/Language/Operators/Array.hs index 6a9d17a35..38ae16333 100644 --- a/copilot-language/src/Copilot/Language/Operators/Array.hs +++ b/copilot-language/src/Copilot/Language/Operators/Array.hs @@ -5,6 +5,7 @@ -- | Combinators to deal with streams carrying arrays. module Copilot.Language.Operators.Array ( (.!!) + , (!) ) where import Copilot.Core ( Typed @@ -16,6 +17,15 @@ import Copilot.Language.Stream (Stream (..)) import Data.Word (Word32) import GHC.TypeLits (KnownNat) +-- | Create a stream that carries an element of an array in another stream. +-- +-- This function implements a projection of the element of an array at a given +-- position, over time. For example, if @s@ is a stream of type @Stream (Array +-- '5 Word8)@, then @s ! 3@ has type @Stream Word8@ and contains the 3rd +-- element (starting from zero) of the arrays in @s@ at any point in time. +(!) :: (KnownNat n, Typed t) + => Stream (Array n t) -> Stream Word32 -> Stream t +arr ! n = Op2 (Index typeOf) arr n -- | Create a stream that carries an element of an array in another stream. -- @@ -23,7 +33,8 @@ import GHC.TypeLits (KnownNat) -- position, over time. For example, if @s@ is a stream of type @Stream (Array -- '5 Word8)@, then @s .!! 3@ has type @Stream Word8@ and contains the 3rd -- element (starting from zero) of the arrays in @s@ at any point in time. +{-# DEPRECATED (.!!) "This function is deprecated in Copilot 4. Use (!)." #-} (.!!) :: ( KnownNat n , Typed t ) => Stream (Array n t) -> Stream Word32 -> Stream t -arr .!! n = Op2 (Index typeOf) arr n +(.!!) = (!) From 70e67d17e876da7760a62cba3f9e3e9d4af85a36 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 6 Sep 2024 11:55:58 +0000 Subject: [PATCH 043/483] copilot-prettyprinter: Update pretty printer access to use the symbol !. Refs #36. A prior commit has introduced a new symbol (!) as the preferred way to project an element of an array for the purposes of reading it. This commit modifies the pretty printer so that it uses the same notation when printing an expression that projects a value of an array. --- copilot-prettyprinter/src/Copilot/PrettyPrint.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs index a92c61668..a8119b12e 100644 --- a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs +++ b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs @@ -99,7 +99,7 @@ ppOp2 op = case op of BwXor _ -> ppInfix "^" BwShiftL _ _ -> ppInfix "<<" BwShiftR _ _ -> ppInfix ">>" - Index _ -> ppInfix ".!!" + Index _ -> ppInfix "!" UpdateField (Struct _) _ f -> \ doc1 doc2 -> parens $ doc1 <+> text "##" <+> text (accessorName f) <+> text "=:" <+> doc2 UpdateField _ _ _ -> impossible "ppOp2" "Copilot.PrettyPrint" From 219bd8647ea193bc523739b149d090369d34cf09 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 6 Sep 2024 11:49:21 +0000 Subject: [PATCH 044/483] copilot: Update array example to use new syntax. Refs #36. A prior commit has introduced a new symbol (!) as the preferred way to project an element of an array for the purposes of reading it. This commit modifies all examples involving arrays to use the new notation. --- copilot/examples/Array.hs | 2 +- copilot/examples/Structs.hs | 4 ++-- copilot/examples/what4/Structs.hs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot/examples/Array.hs b/copilot/examples/Array.hs index c83387f53..fc924c11c 100644 --- a/copilot/examples/Array.hs +++ b/copilot/examples/Array.hs @@ -26,7 +26,7 @@ spec = do -- It passes the current value of arr as an argument. -- The prototype of 'func' would be: -- void func (int8_t arg[3]); - trigger "func" (arr .!! 0) [arg arr] + trigger "func" (arr ! 0) [arg arr] -- Compile the spec main :: IO () diff --git a/copilot/examples/Structs.hs b/copilot/examples/Structs.hs index f3b83b1f3..3b7b01fcc 100644 --- a/copilot/examples/Structs.hs +++ b/copilot/examples/Structs.hs @@ -56,13 +56,13 @@ spec = do -- Check equality, indexing into nested structs and arrays. Note that this is -- trivial by equality. trigger "equalitySameIndex" - ((((battery#volts) .!! 0)#numVolts) == (((battery#volts) .!! 0)#numVolts)) + ((((battery#volts) ! 0)#numVolts) == (((battery#volts) ! 0)#numVolts)) [arg battery] -- Same as previous example, but get a different array index (so should be -- false). trigger "equalityDifferentIndices" - ((((battery#other) .!! 2) .!! 3) == (((battery#other) .!! 2) .!! 4)) + ((((battery#other) ! 2) ! 3) == (((battery#other) ! 2) ! 4)) [arg battery] main :: IO () diff --git a/copilot/examples/what4/Structs.hs b/copilot/examples/what4/Structs.hs index fc61feadf..775572547 100644 --- a/copilot/examples/what4/Structs.hs +++ b/copilot/examples/what4/Structs.hs @@ -57,12 +57,12 @@ spec = do -- Check equality, indexing into nested structs and arrays. Note that this is -- trivial by equality. void $ prop "Example 1" $ forAll $ - (((battery#volts) .!! 0)#numVolts) == (((battery#volts) .!! 0)#numVolts) + (((battery#volts) ! 0)#numVolts) == (((battery#volts) ! 0)#numVolts) -- Same as previous example, but get a different array index (so should be -- false). void $ prop "Example 2" $ forAll $ - (((battery#other) .!! 2) .!! 3) == (((battery#other) .!! 2) .!! 4) + (((battery#other) ! 2) ! 3) == (((battery#other) ! 2) ! 4) -- Update a struct field, then check it for equality. void $ prop "Example 3" $ forAll $ From 6649dfd1249ff36b2540e8da54b6c78fd838b9b9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 2 Sep 2024 17:13:08 +0000 Subject: [PATCH 045/483] copilot-libraries: Rename function to avoid name clash. Refs #36. We want to add support to be able to modify values of elements in arrays. In order to provide a uniform interface for arrays and structs, we have decided to unify the syntax for array and struct accessors, so that one operator symbol indicates read access, and doubling the same operator symbol indicates modification access. For example, (!) would indicate accessing an element of an array to consult it, and (!!) would indicate the same but to update it or modify it. The analogous operators for structs would be (#) and (##). At present, the symbol (!!) is being used for an auxiliary function that indexes a list of streams by a stream of indexes. That function is expensive, and rarely used in real specifications. This commit renames such rarely used function, (!!), to avoid a name clash with our desired name for the function to access elements of arrays in streams for the purposes of modifying them. --- .../src/Copilot/Library/Libraries.hs | 2 +- .../src/Copilot/Library/Utils.hs | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/copilot-libraries/src/Copilot/Library/Libraries.hs b/copilot-libraries/src/Copilot/Library/Libraries.hs index f378063bb..6a217234c 100644 --- a/copilot-libraries/src/Copilot/Library/Libraries.hs +++ b/copilot-libraries/src/Copilot/Library/Libraries.hs @@ -23,6 +23,6 @@ import Copilot.Library.LTL import Copilot.Library.PTLTL import Copilot.Library.Statistics import Copilot.Library.RegExp -import Copilot.Library.Utils +import Copilot.Library.Utils hiding ((!!)) import Copilot.Library.Voting import Copilot.Library.Stacks diff --git a/copilot-libraries/src/Copilot/Library/Utils.hs b/copilot-libraries/src/Copilot/Library/Utils.hs index c3001076a..0200778a4 100644 --- a/copilot-libraries/src/Copilot/Library/Utils.hs +++ b/copilot-libraries/src/Copilot/Library/Utils.hs @@ -13,10 +13,10 @@ module Copilot.Library.Utils -- ** Scans nscanl, nscanr, nscanl1, nscanr1, -- ** Indexing - case', (!!)) + case', (!!), (!!!)) where -import Copilot.Language +import Copilot.Language hiding ((!!)) import qualified Prelude as P -- | Given a stream, produce an infinite list of streams dropping an increasing @@ -135,21 +135,28 @@ case' predicates alternatives = -- -- WARNING: Very expensive! Consider using this only for very short lists. (!!) :: (Typed a, Eq b, Num b, Typed b) => [Stream a] -> Stream b -> Stream a -ls !! n = let indices = map +(!!) = (!!!) +{-# DEPRECATED (!!) "This function is deprecated in Copilot 4. Use (!!!)." #-} + +-- | Index. +-- +-- WARNING: Very expensive! Consider using this only for very short lists. +(!!!) :: (Typed a, Eq b, Num b, Typed b) => [Stream a] -> Stream b -> Stream a +ls !!! n = let indices = map ( constant . fromIntegral ) [ 0 .. P.length ls - 1 ] - select [] _ = last ls - select - ( i : is ) - ( x : xs ) = mux ( i == n ) x ( select is xs ) - -- should not happen - select _ [] = badUsage ("in (!!) defined in Utils.hs " P.++ - "in copilot-libraries") - in if null ls then - badUsage ("in (!!) defined in Utils.hs " P.++ - "indexing the empty list with !! is not defined") - else - select indices ls + select [] _ = last ls + select + ( i : is ) + ( x : xs ) = mux ( i == n ) x ( select is xs ) + -- should not happen + select _ [] = badUsage ("in (!!) defined in Utils.hs " P.++ + "in copilot-libraries") + in if null ls then + badUsage ("in (!!) defined in Utils.hs " P.++ + "indexing the empty list with !! is not defined") + else + select indices ls -- | Cycle a list to form an infinite stream. cycle :: ( Typed a ) => [ a ] -> Stream a From b2cc118e555656aedf621641e44c306140766647 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 6 Aug 2024 08:26:36 -0700 Subject: [PATCH 046/483] copilot-core: Introduce new kind of array element update operator. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit adds a new Op3 that allows array elements to be modified. Co-authored-by: Max Fan --- copilot-core/src/Copilot/Core/Operators.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/src/Copilot/Core/Operators.hs b/copilot-core/src/Copilot/Core/Operators.hs index b8f903ae9..172353ee2 100644 --- a/copilot-core/src/Copilot/Core/Operators.hs +++ b/copilot-core/src/Copilot/Core/Operators.hs @@ -106,3 +106,6 @@ data Op2 a b c where data Op3 a b c d where -- Conditional operator. Mux :: Type a -> Op3 Bool a a a + -- Array operator. + UpdateArray :: Type (Array n t) -> Op3 (Array n t) Word32 t (Array n t) + -- ^ Update an element of an array. From fc47b397c753dd76ade4a76a3bdbbd93a8a8eead Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 6 Aug 2024 08:35:38 -0700 Subject: [PATCH 047/483] copilot-core: Introduce operations to update arrays. Refs #36. Copilot defines a types for fixed-length arrays, but no way to update them. In order to implement array updates for values in streams, we need to have a mechanism to update values in individual arrays. This commit adds a function to arrays to update a value while preserving the length. Co-authored-by: Max Fan --- copilot-core/src/Copilot/Core/Type/Array.hs | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/copilot-core/src/Copilot/Core/Type/Array.hs b/copilot-core/src/Copilot/Core/Type/Array.hs index 8d98c556f..b11e8949b 100644 --- a/copilot-core/src/Copilot/Core/Type/Array.hs +++ b/copilot-core/src/Copilot/Core/Type/Array.hs @@ -3,6 +3,7 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} -- | -- Copyright: (c) 2011 National Institute of Aerospace / Galois, Inc. @@ -14,12 +15,13 @@ module Copilot.Core.Type.Array ( Array , array , arrayElems + , arrayUpdate ) where -- External imports import Data.Proxy (Proxy (..)) -import GHC.TypeLits (KnownNat, Nat, natVal) +import GHC.TypeLits (KnownNat, Nat, natVal, type(-)) -- | Implementation of an array that uses type literals to store length. data Array (n :: Nat) t where @@ -42,3 +44,21 @@ array xs | datalen == typelen = Array xs -- | Return the elements of an array. arrayElems :: Array n a -> [a] arrayElems (Array xs) = xs + +-- | Update element of array to given element. +-- +-- PRE: the second argument denotes a valid index in the array. +arrayUpdate :: Array n a -> Int -> a -> Array n a +arrayUpdate (Array []) _ _ = error errMsg + where + errMsg = "copilot-core: arrayUpdate: Attempt to update empty array" + +arrayUpdate (Array (x:xs)) 0 y = Array (y:xs) + +arrayUpdate (Array (x:xs)) n y = + arrayAppend x (arrayUpdate (Array xs) (n - 1) y) + where + -- | Append to an array while preserving length information at the type + -- level. + arrayAppend :: a -> Array (n - 1) a -> Array n a + arrayAppend x (Array xs) = Array (x:xs) From 9b297a0171d804184435f1d53c5b90821ebf3b27 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 6 Sep 2024 12:29:09 +0000 Subject: [PATCH 048/483] copilot-language: Introduce new kind of array element update operation. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit adds a new operation to the high-level Copilot language that allows updating array elements. It relies on a low-level Op3 that captures the correspnding operation in Copilot Core expressions. In order to provide a uniform interface for arrays and structs, we have decided to unify the syntax for array and struct accessors, so that the operator symbol (=:) is used to set the value of individual elements, and (=$) is used to apply a function to values of elements (at each time). To support using the same syntax for both structs and arrays, this commit moves those operators into a new class, defined in a module Copilot.Language.Operators.Projection. The module Copilot.Language.Operators.Struct now uses that module instead of defining the operators locally, and so does the new module Copilot.Language.Operators.Array. Co-authored-by: Max Fan --- copilot-language/copilot-language.cabal | 1 + .../src/Copilot/Language/Operators/Array.hs | 71 +++++++++++++--- .../Copilot/Language/Operators/Projection.hs | 38 +++++++++ .../src/Copilot/Language/Operators/Struct.hs | 83 ++++++++++--------- 4 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 copilot-language/src/Copilot/Language/Operators/Projection.hs diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 99c5867f2..921ed79b7 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -61,6 +61,7 @@ library , Copilot.Language.Operators.Ord , Copilot.Language.Operators.Temporal , Copilot.Language.Operators.Array + , Copilot.Language.Operators.Projection , Copilot.Language.Operators.Struct , Copilot.Language.Prelude , Copilot.Language.Reify diff --git a/copilot-language/src/Copilot/Language/Operators/Array.hs b/copilot-language/src/Copilot/Language/Operators/Array.hs index 38ae16333..dd5840439 100644 --- a/copilot-language/src/Copilot/Language/Operators/Array.hs +++ b/copilot-language/src/Copilot/Language/Operators/Array.hs @@ -1,22 +1,29 @@ -{-# LANGUAGE Safe #-} - -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE TypeFamilies #-} +-- The following warning is disabled due to a necessary instance of Projectable +-- defined in this module. +{-# OPTIONS_GHC -fno-warn-orphans #-} -- | Combinators to deal with streams carrying arrays. module Copilot.Language.Operators.Array ( (.!!) , (!) + , (!!) + , (=:) + , (=$) ) where -import Copilot.Core ( Typed - , Op2 (Index) - , typeOf - , Array - ) -import Copilot.Language.Stream (Stream (..)) +import Copilot.Core (Array, Op2 (Index), + Op3 (UpdateArray), Typed, typeOf) +import Copilot.Language.Operators.Projection (Projectable(..)) +import Copilot.Language.Stream (Stream (..)) + +import Data.Word (Word32) +import GHC.TypeLits (KnownNat) +import Prelude hiding ((!!)) -import Data.Word (Word32) -import GHC.TypeLits (KnownNat) -- | Create a stream that carries an element of an array in another stream. -- -- This function implements a projection of the element of an array at a given @@ -38,3 +45,45 @@ arr ! n = Op2 (Index typeOf) arr n , Typed t ) => Stream (Array n t) -> Stream Word32 -> Stream t (.!!) = (!) + +-- | Pair a stream with an element accessor, without applying it to obtain the +-- value of the element. +-- +-- This function is needed to refer to an element accessor when the goal is to +-- update the element value, not just to read it. +(!!) :: Stream (Array n t) + -> Stream Word32 + -> Projection (Array n t) (Stream Word32) t +(!!) = ProjectionA + +-- | Update a stream of arrays. + +-- This is an orphan instance; we suppress the warning that GHC would +-- normally produce with a GHC option at the top. +instance (KnownNat n, Typed t) => Projectable (Array n t) (Stream Word32) t where + + -- | A projection of an element of a stream of arrays. + data Projection (Array n t) (Stream Word32) t = + ProjectionA (Stream (Array n t)) (Stream Word32) + + -- | Create a stream where an element of an array has been updated with + -- values from another stream. + -- + -- For example, if an array has two elements of type @Int32@, and @s@ is a + -- stream of such array type (@Stream (Array 2 Int32)@), and $v0$ is a stream + -- of type @Int32@, then @s !! 0 =: v0@ has type @Stream (Array 2 Int32)@ and + -- contains arrays where the value of the first element of each array is that + -- of @v0@ at each point in time, and the value of the second element in the + -- array is the same it had in @s@. + (=:) (ProjectionA s ix) v = Op3 (UpdateArray typeOf) s ix v + + -- | Create a stream where an element of an array has been updated by + -- applying a stream function to it. + -- + -- For example, if an array has two elements of type @Int32@, and @s@ is a + -- stream of such array type (@Stream (Array 2 Int32)@), and $f$ is function + -- of type @Stream Int32 -> Stream Int32@, then @s !! 0 =$ f@ has type + -- @Stream (Array 2 Int32)@ and contains arrays where the value of the first + -- element of each array is that of @f (s ! 0)@ at each point in time, and + -- the value of the second element in the array is the same it had in @s@. + (=$) (ProjectionA s ix) op = Op3 (UpdateArray typeOf) s ix (op (s ! ix)) diff --git a/copilot-language/src/Copilot/Language/Operators/Projection.hs b/copilot-language/src/Copilot/Language/Operators/Projection.hs new file mode 100644 index 000000000..a350e9593 --- /dev/null +++ b/copilot-language/src/Copilot/Language/Operators/Projection.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE TypeFamilies #-} +-- | Interface to access portions of a larger data structure. +-- +-- Default operations to access elements from structs and arrays (e.g., a field +-- of a struct, an element of an array) allow obtaining the values of those +-- elements, but not modifying. +-- +-- This module defines a common interface to manipulate portions of a larger +-- data structure. We define the interface in a generic way, using a type +-- class with two operations: one to set the value of the sub-element, and +-- one to update the value of such element applying a stream function. +module Copilot.Language.Operators.Projection + ( Projectable (..) ) + where + +import Copilot.Language.Stream (Stream) + +infixl 8 =: +infixl 8 =$ + +-- | Common interface to manipulate portions of a larger data structure. +-- +-- A projectable d s t means that it is possible to manipulate a sub-element s +-- of type t carried in a stream of type d.. +class Projectable d s t | d s -> t where + + -- | Unapplied projection or element access on a type. + data Projection d s t + + -- | Modify the value of a sub-element of a type in a stream of elements + -- of that type. + (=:) :: Projection d s t -> Stream t -> Stream d + + -- | Update the value of a sub-element of a type in a stream of elements of + -- that type, by applying a function on streams. + (=$) :: Projection d s t -> (Stream t -> Stream t) -> Stream d diff --git a/copilot-language/src/Copilot/Language/Operators/Struct.hs b/copilot-language/src/Copilot/Language/Operators/Struct.hs index 355da508f..f16005e1f 100644 --- a/copilot-language/src/Copilot/Language/Operators/Struct.hs +++ b/copilot-language/src/Copilot/Language/Operators/Struct.hs @@ -1,4 +1,10 @@ -{-# LANGUAGE Safe #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE TypeFamilies #-} +-- The following warning is disabled due to a necessary instance of Projectable +-- defined in this module. +{-# OPTIONS_GHC -fno-warn-orphans #-} -- | Combinators to deal with streams carrying structs. -- @@ -25,18 +31,17 @@ -- expr = s ## x =$ (+1) -- @ module Copilot.Language.Operators.Struct - ( (#) - , Projection + ( Projectable(..) + , (#) , (##) - , (=:) - , (=$) ) where import Copilot.Core.Type import Copilot.Core.Operators -import Copilot.Language.Stream (Stream (..)) +import Copilot.Language.Operators.Projection +import Copilot.Language.Stream (Stream (..)) -import GHC.TypeLits (KnownSymbol) +import GHC.TypeLits (KnownSymbol) -- | Create a stream that carries a field of a struct in another stream. -- @@ -49,42 +54,44 @@ import GHC.TypeLits (KnownSymbol) => Stream s -> (s -> Field f t) -> Stream t (#) s f = Op1 (GetField typeOf typeOf f) s --- | Type represented an unapplied struct field accessor and a stream carrying --- structs. -data Projection s f t = Projection (Stream s) (s -> Field f t) - -- | Pair a stream with a field accessor, without applying it to obtain the -- value of the field. -- -- This function is needed to refer to a field accessor when the goal is to -- update the field value, not just to read it. (##) :: (KnownSymbol f, Typed t, Typed s, Struct s) - => Stream s -> (s -> Field f t) -> Projection s f t -(##) = Projection + => Stream s -> (s -> Field f t) -> Projection s (s -> Field f t) t +(##) = ProjectionS --- | Create a stream where the field of a struct has been updated with values --- from another stream. --- --- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ --- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and $sT1$ --- is a stream of type @Int32@ then @s ## t2 =: sT1@ has type @Stream T@ and --- contains structs where the value of @t1@ is that of @sT1@ and the value of --- @t2@ is the value that the same field had in @s@, at any point in time. -infixl 8 =: -(=:) :: (KnownSymbol f, Typed t, Typed s, Struct s) - => Projection s f t -> Stream t -> Stream s -(=:) (Projection s f) v = Op2 (UpdateField typeOf typeOf f) s v +-- | Update a stream of structs. --- | Create a stream where the field of a struct has been updated by applying a --- function to it. --- --- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ --- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and $f$ is --- a function from @Stream Int32 -> Stream Int32@ then @s ## t2 =$ f@ has type --- @Stream T@ and contains structs where the value of @t1@ is that of @f@ --- applied to the original value of @t1@ in @s@, and the value of @t2@ is the --- value that the same field had in @s@, at any point in time. -infixl 8 =$ -(=$) :: (KnownSymbol f, Typed t, Typed s, Struct s) - => Projection s f t -> (Stream t -> Stream t) -> Stream s -(=$) (Projection s f) op = s ## f =: (op (s # f)) +-- This is an orphan instance; we suppress the warning that GHC would +-- normally produce with a GHC option at the top. +instance (KnownSymbol f, Typed s, Typed t, Struct s) + => Projectable s (s -> Field f t) t + where + + -- | A projection of a field of a stream of structs. + data Projection s (s -> Field f t) t = ProjectionS (Stream s) (s -> Field f t) + + -- | Create a stream where the field of a struct has been updated with values + -- from another stream. + -- + -- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ + -- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and + -- $sT1$ is a stream of type @Int32@ then @s ## t2 =: sT1@ has type @Stream + -- T@ and contains structs where the value of @t1@ is that of @sT1@ and the + -- value of @t2@ is the value that the same field had in @s@, at any point in + -- time. + (=:) (ProjectionS s f) v = Op2 (UpdateField typeOf typeOf f) s v + + -- | Create a stream where the field of a struct has been updated by applying + -- a function to it. + -- + -- For example, if a struct of type @T@ has two fields, @t1@ of type @Int32@ + -- and @t2@ of type @Word8@, and @s@ is a stream of type @Stream T@, and $f$ + -- is a function from @Stream Int32 -> Stream Int32@ then @s ## t2 =$ f@ has + -- type @Stream T@ and contains structs where the value of @t1@ is that of + -- @f@ applied to the original value of @t1@ in @s@, and the value of @t2@ is + -- the value that the same field had in @s@, at any point in time. + (=$) (ProjectionS s f) op = Op2 (UpdateField typeOf typeOf f) s (op (s # f)) From dc6348749309b95530d1c50c780b052bbb4a5017 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 6 Aug 2024 08:44:04 -0700 Subject: [PATCH 049/483] copilot-c99: Translate array updates to C. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit adds the translation to C of expressions that update an element of an array. Co-authored-by: Max Fan --- copilot-c99/src/Copilot/Compile/C99/Expr.hs | 50 +++++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Expr.hs b/copilot-c99/src/Copilot/Compile/C99/Expr.hs index 40b130086..14e129be9 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Expr.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Expr.hs @@ -13,9 +13,9 @@ import qualified Data.List.NonEmpty as NonEmpty import qualified Language.C99.Simple as C -- Internal imports: Copilot -import Copilot.Core ( Expr (..), Field (..), Op1 (..), Op2 (..), Op3 (..), - Type (..), Value (..), accessorName, arrayElems, - toValues, typeSize ) +import Copilot.Core ( Array, Expr (..), Field (..), Op1 (..), Op2 (..), + Op3 (..), Type (..), Value (..), accessorName, + arrayElems, toValues, typeLength, typeSize ) -- Internal imports import Copilot.Compile.C99.Error ( impossible ) @@ -101,6 +101,47 @@ transExpr (Op2 op e1 e2) = do e2' <- transExpr e2 return $ transOp2 op e1' e2' +transExpr e@(Op3 (UpdateArray arrTy@(Array ty2)) e1 e2 e3) = do + e1' <- transExpr e1 + e2' <- transExpr e2 + e3' <- transExpr e3 + + -- Variable to hold the updated array + (i, _, _) <- get + let varName = "_v" ++ show i + modify (\(i, x, y) -> (i + 1, x, y)) + + -- Add new var decl + let initDecl = C.VarDecln Nothing cTy1 varName Nothing + cTy1 = transType arrTy + modify (\(i, x, y) -> (i, x ++ [initDecl], y)) + + let size :: Type (Array n t) -> C.Expr + size arrTy@(Array ty) = C.LitInt (fromIntegral $ typeLength arrTy) + C..* C.SizeOfType (C.TypeName $ transType ty) + + -- Initialize the var to the same value as the original array + let initStmt = C.Expr $ memcpy (C.Ident varName) e1' (size arrTy) + + -- Update element of array + let updateStmt = case ty2 of + Array _ -> C.Expr $ memcpy dest e3' size + where + dest = C.Index (C.Ident varName) e2' + size = C.LitInt + (fromIntegral $ typeSize ty2) + C..* C.SizeOfType (C.TypeName (tyElemName ty2)) + + _ -> C.Expr + $ C.AssignOp + C.Assign + (C.Index (C.Ident varName) e2') + e3' + + modify (\(i, x, y) -> (i, x, y ++ [ initStmt, updateStmt ])) + + return $ C.Ident varName + transExpr (Op3 op e1 e2 e3) = do e1' <- transExpr e1 e2' <- transExpr e2 @@ -179,7 +220,8 @@ transOp2 op e1 e2 = case op of -- expression. transOp3 :: Op3 a b c d -> C.Expr -> C.Expr -> C.Expr -> C.Expr transOp3 op e1 e2 e3 = case op of - Mux _ -> C.Cond e1 e2 e3 + Mux _ -> C.Cond e1 e2 e3 + UpdateArray _ -> impossible "transOp3" "copilot-c99" -- | Translate @'Abs' e@ in Copilot Core into a C99 expression. -- From 04a8964d9eca57c060f753db0753c3ff8302c167 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 6 Aug 2024 08:49:00 -0700 Subject: [PATCH 050/483] copilot-interpreter: Interpret array update expressions. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit adds the interpretation in Haskell of expressions that update an element of an array in a stream of arrays. Co-authored-by: Max Fan --- copilot-interpreter/src/Copilot/Interpret/Eval.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/copilot-interpreter/src/Copilot/Interpret/Eval.hs b/copilot-interpreter/src/Copilot/Interpret/Eval.hs index 3d8df44a7..462b5853e 100644 --- a/copilot-interpreter/src/Copilot/Interpret/Eval.hs +++ b/copilot-interpreter/src/Copilot/Interpret/Eval.hs @@ -19,8 +19,8 @@ module Copilot.Interpret.Eval import Copilot.Core (Expr (..), Field (..), Id, Name, Observer (..), Op1 (..), Op2 (..), Op3 (..), Spec, Stream (..), Trigger (..), Type (..), UExpr (..), Value (..), - arrayElems, specObservers, specStreams, - specTriggers, updateField) + arrayElems, arrayUpdate, specObservers, + specStreams, specTriggers, updateField) import Copilot.Interpret.Error (badUsage) import Prelude hiding (id) @@ -32,6 +32,7 @@ import Data.Dynamic (Dynamic, fromDynamic, toDyn) import Data.List (transpose) import Data.Maybe (fromJust) import Data.Typeable (Typeable) +import GHC.TypeLits (KnownNat, Nat, natVal) -- | Exceptions that may be thrown during interpretation of a Copilot -- specification. @@ -265,7 +266,8 @@ catchZero f x y = f x y -- Haskell function operating on the same types as the -- 'Copilot.Core.Operators.Op3'. evalOp3 :: Op3 a b c d -> (a -> b -> c -> d) -evalOp3 (Mux _) = \ !v !x !y -> if v then x else y +evalOp3 (Mux _) = \ !v !x !y -> if v then x else y +evalOp3 (UpdateArray ty) = \xs n x -> arrayUpdate xs (fromIntegral n) x -- | Turn a stream into a key-value pair that can be added to an 'Env' for -- simulation. From 1dfc4dd020978e6d8e9789c185011080f37f9b3b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 6 Aug 2024 08:50:27 -0700 Subject: [PATCH 051/483] copilot-prettyprinter: Pretty-print array update expressions. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit add support to pretty-print expressions that update elements of an array. Co-authored-by: Max Fan --- copilot-prettyprinter/src/Copilot/PrettyPrint.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs index a8119b12e..64ca9c90a 100644 --- a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs +++ b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs @@ -111,6 +111,8 @@ ppOp3 op = case op of text "(if" <+> doc1 <+> text "then" <+> doc2 <+> text "else" <+> doc3 <> text ")" + UpdateArray _ -> \ doc1 doc2 doc3 -> + parens $ doc1 <+> text "!!" <+> doc2 <+> text "=:" <+> doc3 -- | Parenthesize two 'Doc's, separated by an infix 'String'. ppInfix :: String -> Doc -> Doc -> Doc From a5d50f9571c67f404e96cb3e5d6f6e54efb5193f Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 12 Aug 2024 06:29:44 -0400 Subject: [PATCH 052/483] copilot-theorem: Translate array updates to What4. Refs #36. Copilot currently supports reading values from arrays, but not changing them. This commit adds symbolic reasoning support for array update expressions by translating them to What4. The implementation is largely inspired by the existing translation for array index expressions. Because the index in an array update expression can be symbolic, we must build a chain of symbolic if-then-else expressions that check the value of the possibly-symbolic index against all possible concrete index values from `0` to `n - 1` (where `n` is the size of the array). --- .../src/Copilot/Theorem/What4/Translate.hs | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs index 5b84b021b..f6ced33b7 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs @@ -52,16 +52,18 @@ import Data.Maybe (fromJust) import Data.Parameterized.Classes (KnownRepr (..)) import Data.Parameterized.Context (EmptyCtx, type (::>)) import Data.Parameterized.NatRepr (LeqProof (..), NatCases (..), - NatRepr, decNat, isZeroOrGT1, + NatRepr, decNat, incNat, + intValue, isZeroOrGT1, knownNat, minusPlusCancel, - mkNatRepr, testNatCases) + mkNatRepr, testNatCases, + testStrictLeq) import Data.Parameterized.Some (Some (..)) import Data.Parameterized.SymbolRepr (SymbolRepr, knownSymbol) import qualified Data.Parameterized.Vector as V import Data.Type.Equality (TestEquality (..), (:~:) (..)) import Data.Word (Word32) import GHC.TypeLits (KnownSymbol) -import GHC.TypeNats (KnownNat, type (<=)) +import GHC.TypeNats (KnownNat, type (<=), type (+)) import qualified Panic as Panic import qualified What4.BaseTypes as WT @@ -1141,7 +1143,37 @@ translateOp3 :: forall sym a b c d . translateOp3 sym origExpr op xe1 xe2 xe3 = case (op, xe1, xe2, xe3) of (CE.Mux _, XBool te, xe1, xe2) -> liftIO $ mkIte sym te xe1 xe2 (CE.Mux _, _, _, _) -> unexpectedValues "mux operation" + (CE.UpdateArray _, xe1, xe2, xe3) -> translateUpdateArray xe1 xe2 xe3 where + -- Translate an 'CE.UpdateArray' operation and its arguments into a what4 + -- representation. This checks that the first argument is an 'XArray' and + -- the second argument is an 'XWord32', invoking 'panic' is this invariant + -- is not upheld. + -- + -- Note: Currently, copilot only checks if array indices are out of bounds + -- as a side condition. The method of translation we are using simply + -- creates a nest of if-then-else expression to check the index expression + -- against all possible indices. If the index expression is known by the + -- solver to be out of bounds (for instance, if it is a constant 5 for an + -- array of 5 elements), then the if-then-else will trivially resolve to + -- true. + translateUpdateArray :: XExpr sym + -> XExpr sym + -> XExpr sym + -> TransM sym (XExpr sym) + translateUpdateArray xe1 xe2 newXe = case (xe1, xe2) of + (XArray xes, XWord32 ix) -> do + -- The second argument should not be out of bounds (i.e., greater than + -- or equal to the length of the array) + xesLenBV <- liftIO $ WI.bvLit sym knownNat $ BV.mkBV knownNat + $ toInteger $ V.lengthInt xes + inRange <- liftIO $ WI.bvUlt sym ix xesLenBV + addSidePred inRange + + xes' <- liftIO $ buildUpdateArrayExpr sym xes ix newXe + pure $ XArray xes' + _ -> unexpectedValues "update array operation" + unexpectedValues :: forall m x . (Panic.HasCallStack, MonadIO m) => String -> m x unexpectedValues op = @@ -1180,6 +1212,52 @@ buildIndexExpr sym ix = loop 0 ixEq <- WI.bvEq sym curIxExpr ix mkIte sym ixEq xe rstExpr +-- | Construct an expression that updates an array element at a particular index +-- by building a chain of @if@ expressions, where each expression checks if the +-- current index is equal to a given index in the array. If the indices are +-- equal, return the array with the element at that index updated. Otherwise, +-- proceed to the next @if@ expression, which checks the next index in the +-- array. +buildUpdateArrayExpr :: forall sym n. + (1 <= n, WFP.IsInterpretedFloatExprBuilder sym) + => sym + -> V.Vector n (XExpr sym) + -- ^ Elements + -> WI.SymBV sym 32 + -- ^ Index + -> XExpr sym + -- ^ New element + -> IO (V.Vector n (XExpr sym)) +buildUpdateArrayExpr sym xelts ix newXe = loop (knownNat @0) + where + n :: NatRepr n + n = V.length xelts + + n32 :: NatRepr 32 + n32 = knownNat @32 + + loop :: forall i. + ((i + 1) <= n) + => NatRepr i + -> IO (V.Vector n (XExpr sym)) + loop curIx = + case testStrictLeq nextIx n of + -- Recursive case + Left LeqProof -> do + rstExpr <- loop nextIx + curIxExpr <- WI.bvLit sym n32 $ BV.mkBV n32 $ intValue curIx + ixEq <- WI.bvEq sym curIxExpr ix + V.zipWithM (mkIte sym ixEq) newXelts rstExpr + -- Base case, we are at the last possible index (n - 1) + Right Refl -> + pure newXelts + where + nextIx :: NatRepr (i + 1) + nextIx = incNat curIx + + newXelts :: V.Vector n (XExpr sym) + newXelts = V.insertAt curIx newXe xelts + -- | Construct an @if@ expression of the appropriate type. mkIte :: WFP.IsInterpretedFloatExprBuilder sym => sym From b776616b44873f7ca63a6921a6705df34d5cd224 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 6 Sep 2024 11:52:53 +0000 Subject: [PATCH 053/483] copilot: Update array example to demonstrate array updates. Refs #36. Previous commits have introduced support to modify individual elements of arrays, either by giving a new stream of values for a position in an array, or by applying a stream function to an element. This commit updates an existing example in the examples/ directory to demonstrate array updates. --- copilot/examples/Array.hs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/copilot/examples/Array.hs b/copilot/examples/Array.hs index fc924c11c..7f43a21b1 100644 --- a/copilot/examples/Array.hs +++ b/copilot/examples/Array.hs @@ -28,6 +28,20 @@ spec = do -- void func (int8_t arg[3]); trigger "func" (arr ! 0) [arg arr] + -- A trigger that fires 'func2' every time. + -- It passes the current value of arr as an argument, but updating the first + -- element of the array to always be True. + -- The prototype of 'func2' would be: + -- void func2 (int8_t arg[3]); + trigger "func2" true [arg (arr !! 0 =: true)] + + -- A trigger that fires 'func2' every time. + -- It passes the current value of arr as an argument, but negating the second + -- element of the array. + -- The prototype of 'func3' would be: + -- void func3 (int8_t arg[3]); + trigger "func3" true [arg (arr !! 1 =$ not)] + -- Compile the spec main :: IO () main = interpret 30 spec From 8e3bbba65288297a474835c389529488cde231ac Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:21:31 -0700 Subject: [PATCH 054/483] copilot: Introduce example reasoning about array updates. Refs #36. Previous commits have introduced support to modify individual elements of arrays, either by giving a new stream of values for a position in an array, or by applying a stream function to an element. This commit introduces a new example in the examples/ directory to demonstrate using copilot-theorem to reason about arrays where individual elements are being updated. --- copilot/examples/what4/Arrays.hs | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 copilot/examples/what4/Arrays.hs diff --git a/copilot/examples/what4/Arrays.hs b/copilot/examples/what4/Arrays.hs new file mode 100644 index 000000000..3fd74544a --- /dev/null +++ b/copilot/examples/what4/Arrays.hs @@ -0,0 +1,42 @@ +-- | An example showing the usage of the What4 backend in copilot-theorem for +-- arrays where individual elements are updated. + +{-# LANGUAGE DataKinds #-} + +module Main where + +import qualified Prelude as P +import Control.Monad (void, forM_) + +import Language.Copilot +import Copilot.Theorem.What4 + +spec :: Spec +spec = do + let pair :: Stream (Array 2 Int16) + pair = extern "pair" Nothing + + -- Check equality, indexing into array and modifying the value. Note that + -- this is trivial by equality. + void $ prop "Example 1" $ forAll $ + ((pair !! 0 =$ (+1)) ! 0) == ((pair ! 0) + 1) + + -- Same as previous example, but get a different array index (so should be + -- false). + void $ prop "Example 2" $ forAll $ + ((pair !! 0 =$ (+1)) ! 1) == ((pair ! 0) + 1) + +main :: IO () +main = do + spec' <- reify spec + + -- Use Z3 to prove the properties. + results <- prove Z3 spec' + + -- Print the results. + forM_ results $ \(nm, res) -> do + putStr $ nm <> ": " + case res of + Valid -> putStrLn "valid" + Invalid -> putStrLn "invalid" + Unknown -> putStrLn "unknown" From 165e43094946e9f06efc61e50c8ccd8477d903dc Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:22:54 -0700 Subject: [PATCH 055/483] copilot-core: Document changes in CHANGELOG. Refs #36. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index b912c7e51..e55e962c8 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2024-09-03 + * Update Op3, Array to support array updates. (#36) + 2024-07-07 * Version bump (3.20). (#522) * Update Op2, Struct to support struct field updates. (#520) From 2bc48dc7226449e2e840841eb317e2bd7e71144a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:24:16 -0700 Subject: [PATCH 056/483] copilot-language: Document changes in CHANGELOG. Refs #36. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index bc428fddc..55b56088e 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2024-09-03 + * Add support for array updates. (#36) + 2024-07-07 * Version bump (3.20). (#522) * Remove deprecated function Copilot.Language.Spec.forall. (#518) From 2d60f80951b9d642fc9ef0654b03ab5b62afa168 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:24:43 -0700 Subject: [PATCH 057/483] copilot-c99: Document changes in CHANGELOG. Refs #36. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 836ff3415..5c68efdab 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2024-09-03 + * Add support for array updates. (#36) + 2024-07-07 * Version bump (3.20). (#522) * Add support for struct field updates. (#520) From 7a1a3716c41012f58905d500f9b7ea377077dfc1 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:25:17 -0700 Subject: [PATCH 058/483] copilot-interpreter: Document changes in CHANGELOG. Refs #36. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index e3bfe6d92..22b5915f0 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2024-09-03 + * Add support for array updates. (#36) + 2024-07-07 * Version bump (3.20). (#522) * Add support for struct field updates. (#520) From b868e9392b22ecb10f1fe5ce643046b7f6dd3eec Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:25:48 -0700 Subject: [PATCH 059/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #36. --- copilot-prettyprinter/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index d91bb375e..d2b10e690 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,5 +1,6 @@ -2024-08-30 +2024-09-03 * Add support for pretty-printing struct update expressions. (#526) + * Add support for pretty-printing array update expressions. (#36) 2024-07-07 * Version bump (3.20). (#522) From 02c89d20b8bd2ee86848a19e2bacae35aeb1308c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:26:41 -0700 Subject: [PATCH 060/483] copilot-libraries: Document changes in CHANGELOG. Refs #36. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index d93218fa5..7d807efb0 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2024-09-03 + * Rename operator to avoid name clash. (#36) + 2024-07-07 * Version bump (3.20). (#522) From f7fc2de590205715be0711e7e2ffc1981baf84e4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:27:16 -0700 Subject: [PATCH 061/483] copilot-theorem: Document changes in CHANGELOG. Refs #36. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index efc0d0bcf..3f9269bdb 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ -2024-08-30 +2024-09-03 * Add support for struct updates in Copilot.Theorem.What4. (#524) + * Add support for array updates in Copilot.Theorem.What4. (#36) 2024-07-07 * Version bump (3.20). (#522) From 191a75f096d5956f25737b354a1279b00a6e2cde Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 3 Sep 2024 08:27:45 -0700 Subject: [PATCH 062/483] copilot: Document changes in CHANGELOG. Refs #36. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index c71906891..1da30d3ee 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,6 @@ -2024-08-30 +2024-09-03 * Update example to demonstrate struct update support. (#524) + * Update example to demonstrate array update support. (#36) 2024-07-07 * Version bump (3.20). (#522) From ccfb96e06de3f01259c7b885316e3690521fb644 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 8 Sep 2024 05:43:32 +0000 Subject: [PATCH 063/483] Bump versions and constraints. Refs #532. --- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 12 ++++++------ copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot/copilot.cabal | 14 +++++++------- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index ca3cf1d15..2acefad8c 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 3.20 +version : 4.0 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 3.20 && < 3.21 + , copilot-core >= 4.0 && < 4.1 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index b49a0629a..500c97df8 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 3.20 +version: 4.0 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 1f844fcfa..9ad90e75e 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 3.20 +version: 4.0 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.20 && < 3.21 + copilot-core >= 4.0 && < 4.1 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 921ed79b7..a9b79547f 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 3.20 +version: 4.0 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 3.20 && < 3.21 - , copilot-interpreter >= 3.20 && < 3.21 - , copilot-theorem >= 3.20 && < 3.21 + , copilot-core >= 4.0 && < 4.1 + , copilot-interpreter >= 4.0 && < 4.1 + , copilot-theorem >= 4.0 && < 4.1 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index ad89218d1..741a4cc8e 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 3.20 +version: 4.0 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -36,12 +36,12 @@ library hs-source-dirs: src - build-depends: base >= 4.9 && < 5 + build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.7 - , mtl >= 2.0 && < 2.4 - , parsec >= 2.0 && < 3.2 - , copilot-language >= 3.20 && < 3.21 + , containers >= 0.4 && < 0.7 + , mtl >= 2.0 && < 2.4 + , parsec >= 2.0 && < 3.2 + , copilot-language >= 4.0 && < 4.1 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 0f452c68d..73746675a 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 3.20 +version: 4.0 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 3.20 && < 3.21 + copilot-core >= 4.0 && < 4.1 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 496ef8c32..46416e9ee 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 3.20 +version : 4.0 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 3.20 && < 3.21 - , copilot-prettyprinter >= 3.20 && < 3.21 + , copilot-core >= 4.0 && < 4.1 + , copilot-prettyprinter >= 4.0 && < 4.1 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index cfc61c628..394431a33 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 3.20 +version: 4.0 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.5 - , copilot-core >= 3.20 && < 3.21 - , copilot-theorem >= 3.20 && < 3.21 - , copilot-language >= 3.20 && < 3.21 - , copilot-libraries >= 3.20 && < 3.21 - , copilot-c99 >= 3.20 && < 3.21 - , copilot-prettyprinter >= 3.20 && < 3.21 + , copilot-core >= 4.0 && < 4.1 + , copilot-theorem >= 4.0 && < 4.1 + , copilot-language >= 4.0 && < 4.1 + , copilot-libraries >= 4.0 && < 4.1 + , copilot-c99 >= 4.0 && < 4.1 + , copilot-prettyprinter >= 4.0 && < 4.1 exposed-modules: Language.Copilot, Language.Copilot.Main From 2cea27decb7c62b375800f8b4bc1fdfa78433034 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 8 Sep 2024 05:47:15 +0000 Subject: [PATCH 064/483] Document changes in CHANGELOG. Refs #532. --- copilot-c99/CHANGELOG | 3 ++- copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 ++- copilot-language/CHANGELOG | 3 ++- copilot-libraries/CHANGELOG | 3 ++- copilot-prettyprinter/CHANGELOG | 3 ++- copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 5c68efdab..406051dd8 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Add support for array updates. (#36) 2024-07-07 diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index e55e962c8..db1a795be 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Update Op3, Array to support array updates. (#36) 2024-07-07 diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 22b5915f0..5cb1ad93c 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Add support for array updates. (#36) 2024-07-07 diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 55b56088e..c4f11fd36 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Add support for array updates. (#36) 2024-07-07 diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 7d807efb0..9bdfd287a 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Rename operator to avoid name clash. (#36) 2024-07-07 diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index d2b10e690..e5409a927 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Add support for pretty-printing struct update expressions. (#526) * Add support for pretty-printing array update expressions. (#36) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 3f9269bdb..fa1870c7f 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Add support for struct updates in Copilot.Theorem.What4. (#524) * Add support for array updates in Copilot.Theorem.What4. (#36) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 1da30d3ee..16965d835 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2024-09-03 +2024-09-07 + * Version bump (4.0). (#532) * Update example to demonstrate struct update support. (#524) * Update example to demonstrate array update support. (#36) From 2f763ca634a1a45fe62191ec1bec8b7ddba4abe4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 4 Oct 2024 12:47:05 +0000 Subject: [PATCH 065/483] copilot: Update contribution guidelines. Refs #476. Copilot now has a process that allows us to accept contributions from external users again. This commit updates the README to describe the new process that users should follow to contribute to Copilot. I am choosing to leave some details out to not overwhelm users in the README; that can be discussed later when the PR is processed. --- copilot/README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/copilot/README.md b/copilot/README.md index aef1fb26b..e5ab5572b 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -281,12 +281,34 @@ tutorial, please visit the Copilot website: # Contributions [(Back to top)](#table-of-contents) -Copilot cannot accept pull requests or code contributions from developers -outside the development team at this point. - -If you have a question, find a bug, or would like to request a change, please -file an issue adding as much information as you can to help us reproduce the -error or identify the use case. Please file the issue with no labels. +We'd love to receive your contributions, be it code fixes, new features, bug +reports, discussions, or anything else that can help the Copilot project. + +If you have any comments, questions, ideas, or other topics that you think may +be of interest, start a new discussion +[here](https://github.com/Copilot-Language/copilot/discussions). + +If you would like to contribute a fix for an issue, please comment on the issue +indicating that you want to fix it so that we can assign it to you and track +the status on our end. If the issue does not exist, create it first or ask that +an existing discussion be promoted to an issue. + +If you are unsure about whether your submission should be filed as an issue or +as a discussion, file it as a discussion. We can always move it later. + +To facilitate merging any pull requests that you send, please: +- Reference the issue you are addressing with the text `Refs #.` + at the end of the subject line of each commit message, in *every commit*. + Replace `` with the number of the specific issue that your pull + request is addressing. +- Describe what each commit does individually *in the commit's message*. It's + best to err on the side of being more descriptive than less. +- Update the CHANGELOGs in the *last commit(s)*. + +You can take a look at the repository's [commit +history](https://github.com/Copilot-Language/copilot/commits/master/) to better +understand the process we follow. Click on each commit to see how we write +commit messages. # Acknowledgements [(Back to top)](#table-of-contents) From ee6bf33faa742c6ffd0349947de33de683cdb659 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 4 Oct 2024 12:49:42 +0000 Subject: [PATCH 066/483] copilot: Document changes in CHANGELOG. Refs #476. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 16965d835..6d84bf465 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-04 + * Update contribution guidelines (#476). + 2024-09-07 * Version bump (4.0). (#532) * Update example to demonstrate struct update support. (#524) From 58610b45cc9702cbcfb1410ef2fa033712b2755b Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 27 Sep 2024 14:04:43 -0400 Subject: [PATCH 067/483] copilot-language: Detect duplicate extern names in properties and theorems. Refs #536. The Copilot analysis functionality in `Copilot.Language.Analyze` was able to detect distinct externs with the same name (something that should be rejected with a proper error message) in triggers and observers, but not in properties or theorems. As a consequence of this, `copilot-theorem` could outright crash when attempting to prove properties in a specification with duplicate extern names. This fixes the issue by extending the analysis functionality to look into properties and theorems as well. --- .../src/Copilot/Language/Analyze.hs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/copilot-language/src/Copilot/Language/Analyze.hs b/copilot-language/src/Copilot/Language/Analyze.hs index 9d326395c..04de14ed4 100644 --- a/copilot-language/src/Copilot/Language/Analyze.hs +++ b/copilot-language/src/Copilot/Language/Analyze.hs @@ -17,6 +17,7 @@ import qualified Copilot.Core as C import Copilot.Language.Stream (Stream (..)) import Copilot.Language.Spec import Copilot.Language.Error (badUsage) +import Copilot.Theorem.Prove (UProof) import Data.List (groupBy) import Data.IORef @@ -268,10 +269,16 @@ analyzeExts ExternEnv { externVarEnv = vars -- | Obtain all the externs in a specification. specExts :: IORef Env -> Spec' a -> IO ExternEnv specExts refStreams spec = do - env <- foldM triggerExts - (ExternEnv [] [] [] []) - (triggers $ runSpec spec) - foldM observerExts env (observers $ runSpec spec) + env0 <- foldM triggerExts + (ExternEnv [] [] [] []) + (triggers $ runSpec spec) + env1 <- foldM observerExts + env0 + (observers $ runSpec spec) + env2 <- foldM propertyExts + env1 + (properties $ runSpec spec) + foldM theoremExts env2 (theorems $ runSpec spec) where observerExts :: ExternEnv -> Observer -> IO ExternEnv @@ -283,6 +290,12 @@ specExts refStreams spec = do foldM (\env'' (Arg arg_) -> collectExts refStreams arg_ env'') env' args + propertyExts :: ExternEnv -> Property -> IO ExternEnv + propertyExts env (Property _ stream) = collectExts refStreams stream env + + theoremExts :: ExternEnv -> (Property, UProof) -> IO ExternEnv + theoremExts env (p, _) = propertyExts env p + -- | Obtain all the externs in a stream. collectExts :: C.Typed a => IORef Env -> Stream a -> ExternEnv -> IO ExternEnv collectExts refStreams stream_ env_ = do From 7873c0287914b9047ff93b42373a7ac58cbc7c49 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 27 Sep 2024 14:42:16 -0400 Subject: [PATCH 068/483] copilot-language: Document changes in CHANGELOG. Refs #536. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index c4f11fd36..73789c0c6 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-15 + * Reject duplicate externs in properties and theorems. (#536) + 2024-09-07 * Version bump (4.0). (#532) * Add support for array updates. (#36) From b5d8f37b126c07f24012e6b0ed5ea9f688a6e1d2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 15 Oct 2024 01:57:17 +0000 Subject: [PATCH 069/483] copilot: Update README with missing publications. Refs #544. The current publications section of the README is missing several publications related to Copilot published by our team, including most recent publications. This commit updates the list to include all new papers, several older papers, and any known thesis. --- copilot/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/copilot/README.md b/copilot/README.md index e5ab5572b..870cca956 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -257,6 +257,28 @@ is: Other relevant papers include: +- [Runtime Verification in Real-Time with the Copilot Language: A Tutorial](https://link.springer.com/chapter/10.1007/978-3-031-71177-0_27) + +- [Trustworthy Runtime Verification via Bisimulation (Experience Report)](https://dl.acm.org/doi/abs/10.1145/3607841) + +- [An Introduction to Copilot](https://copilot-language.github.io/downloads/copilot_tutorial.pdf) + +- [Assuring the Guardians](https://link.springer.com/chapter/10.1007/978-3-319-23820-3_6) + +- [Experience report: a do-it-yourself high-assurance compiler](https://dl.acm.org/doi/abs/10.1145/2364527.2364553) + +- [Compiling an Haskell EDSL to C: A new C back-end for the Copilot runtime verification framework](https://studenttheses.uu.nl/handle/20.500.12932/29176) + +- [Challenges in High-Assurance Runtime Verification](https://link.springer.com/chapter/10.1007/978-3-319-47166-2_31) + +- [Runtime Monitoring On Hard Real-Time Operating Systems](http://hdl.handle.net/10342/4999) + +- [Design and Testing of an Approach to Automated In-Flight Safety Risk Management for sUAS Operations](https://ntrs.nasa.gov/citations/20220005948) + +- [The Essence of Reactivity](https://dl.acm.org/doi/10.1145/3609026.3609727) + +- [Types that Change: The Extensible Type Design Pattern](https://dl.acm.org/doi/abs/10.1145/3609025.3609475) + - [Automated Translation of Natural Language Requirements to Runtime Monitors](https://link.springer.com/chapter/10.1007/978-3-030-99524-9_21) - [Copilot: A Hard Real-Time Runtime Monitor](https://link.springer.com/chapter/10.1007/978-3-642-16612-9_26) From 9753a5b42eda91e938b22ac7cf65132c8e1d5024 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 15 Oct 2024 01:57:58 +0000 Subject: [PATCH 070/483] copilot: Document changes in CHANGELOG. Refs #544. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 6d84bf465..c7f474576 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,6 @@ -2024-10-04 +2024-10-14 * Update contribution guidelines (#476). + * Update README with missing publications. (#544) 2024-09-07 * Version bump (4.0). (#532) From ceae6480015fdb4b005b545138a2d31846033bbc Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 27 Sep 2024 15:17:51 -0400 Subject: [PATCH 071/483] copilot: Update what4-propositional example's comments to match results. Refs #535. Previously, the comments in the `what4-propositional` example claimed that certain examples were unprovable, even though they were actually provable. This is a result of the heuristics in `Copilot.Theorem.What4` becoming more intelligent after commit 4ae3f249fbf8c37c0909046150ff341d432bd042, but due to an oversight, the comments in the `what4-propositional` example were not updated at the time. This commit revamps the `what4-propositional` example so that each example's comments clearly and accurately state whether the example is provable with the current implementation of `Copilot.Theorem.What4`. I have organized the examples so that they are organized into logically separate sections, with each section requiring increasingly more sophisticated techniques to prove (or, in some cases, fail to prove). --- copilot/examples/what4/Propositional.hs | 65 +++++++++++++++++-------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/copilot/examples/what4/Propositional.hs b/copilot/examples/what4/Propositional.hs index 75dcf55fc..4caf669d4 100644 --- a/copilot/examples/what4/Propositional.hs +++ b/copilot/examples/what4/Propositional.hs @@ -11,40 +11,65 @@ import Copilot.Theorem.What4 spec :: Spec spec = do + -- * Non-inductive propositions + -- The constant value true, which is translated as the corresponding SMT - -- boolean literal. + -- boolean literal (and is therefore provable). void $ prop "Example 1" (forAll true) -- The constant value false, which is translated as the corresponding SMT - -- boolean literal. + -- boolean literal (and is therefore not provable). void $ prop "Example 2" (forAll false) - -- An inductively defined flavor of true, which requires induction to prove, - -- and hence is found to be invalid by the SMT solver (since no inductive - -- hypothesis is made). + -- An "a or not a" proposition which does not require any sort of inductive + -- argument (but see examples 5 and 6 below for versions that do require + -- induction to solve). This is easily proven. + let a = [False] ++ b + b = not a + void $ prop "Example 3" (forAll (a || b)) + + -- An "a or not a" proposition using external streams, which is also provable. + let a = extern "a" Nothing + void $ prop "Example 4" (forAll (a || not a)) + + -- * Simple inductive propositions + -- + -- While Copilot.Theorem.What4 is not able to solve all inductive propositions + -- in general (see the "Complex inductive propositions" section below), the + -- following inductive propositions are simple enough that the heuristics in + -- Copilot.Theorem.What4 can solve them without issue. + + -- An inductively defined flavor of true. let a = [True] ++ a - void $ prop "Example 3" (forAll a) + void $ prop "Example 5" (forAll a) - -- An inductively defined "a or not a" proposition, which is unprovable by - -- the SMT solver. + -- An inductively defined "a or not a" proposition (i.e., a more complex + -- version of example 3 above). let a = [False] ++ b b = [True] ++ a - void $ prop "Example 4" (forAll (a || b)) - - -- A version of "a or not a" proposition which does not require any sort of - -- inductive argument, and hence is provable. - let a = [False] ++ b - b = not a - void $ prop "Example 5" (forAll (a || b)) + void $ prop "Example 6" (forAll (a || b)) - -- A bit more convoluted version of Example 5, which is provable. + -- A bit more convoluted version of example 6. let a = [True, False] ++ b b = [False] ++ not (drop 1 a) - void $ prop "Example 6" (forAll (a || b)) + void $ prop "Example 7" (forAll (a || b)) - -- An example using external streams. - let a = extern "a" Nothing - void $ prop "Example 7" (forAll (a || not a)) + -- * Complex induction propositions + -- + -- The heuristics in Copilot.Theorem.What4 are not able to prove these + -- inductive propositions, so these will be reported as unprovable, even + -- though each proposition is actually provable. + + -- An inductively defined flavor of true (i.e., a more complex version of + -- example 5 above). + let a = [True] ++ ([True] ++ ([True] ++ a)) + void $ prop "Example 8" (forAll a) + + -- An inductively defined "a or not a" proposition (i.e., a more complex + -- version of example 6 above). + let a = [False] ++ ([False] ++ ([False] ++ b)) + b = [True] ++ ([True] ++ ([True] ++ a)) + void $ prop "Example 9" (forAll (a || b)) main :: IO () main = do From b71e159738b215bb35bf5fbd1e073d4d25c30d00 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 27 Sep 2024 15:20:03 -0400 Subject: [PATCH 072/483] copilot: Document changes in CHANGELOG. Refs #535. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index c7f474576..fc36b32d4 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,6 +1,7 @@ -2024-10-14 +2024-10-18 * Update contribution guidelines (#476). * Update README with missing publications. (#544) + * Make the what4-propositional example's comments match results. (#535) 2024-09-07 * Version bump (4.0). (#532) From 2dc9fb4984b829b55425aed5adc7c574edca7aae Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 30 Sep 2024 15:33:28 -0400 Subject: [PATCH 073/483] copilot: Give example of how to implement updateField. Refs #525. This adds an `examples/StructsUpdateField.hs` example that (1) describes how to implement `updateField` for some example `Struct` instances, and (2) demonstrates that `updateField` works by using it with the Copilot interpreter. --- copilot/copilot.cabal | 12 ++ copilot/examples/Structs.hs | 7 ++ copilot/examples/StructsUpdateField.hs | 159 +++++++++++++++++++++++++ copilot/examples/what4/Structs.hs | 7 ++ 4 files changed, 185 insertions(+) create mode 100644 copilot/examples/StructsUpdateField.hs diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 394431a33..467ae50c9 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -207,6 +207,18 @@ executable structs else buildable: False +executable structs-update-field + main-is: StructsUpdateField.hs + hs-source-dirs: examples + build-depends: base >= 4.9 && < 5 + , copilot + , copilot-c99 + default-language: Haskell2010 + if flag(examples) + buildable: True + else + buildable: False + executable voting main-is: Voting.hs hs-source-dirs: examples diff --git a/copilot/examples/Structs.hs b/copilot/examples/Structs.hs index 3b7b01fcc..559a5be29 100644 --- a/copilot/examples/Structs.hs +++ b/copilot/examples/Structs.hs @@ -23,6 +23,11 @@ instance Struct Volts where toValues volts = [ Value Word16 (numVolts volts) , Value Bool (flag volts) ] + -- Note that we do not implement `updateField` here. `updateField` is only + -- needed to make updates to structs work in the Copilot interpreter, and we + -- do not use the interpreter in this example. (See + -- `examples/StructsUpdateField.hs` for an example that does implement + -- `updateField`.) -- | `Volts` instance for `Typed`. instance Typed Volts where @@ -41,6 +46,8 @@ instance Struct Battery where , Value typeOf (volts battery) , Value typeOf (other battery) ] + -- Note that we do not implement `updateField` here for the same reasons as in + -- the `Struct Volts` instance above. -- | `Battery` instance for `Typed`. Note that `undefined` is used as an -- argument to `Field`. This argument is never used, so `undefined` will never diff --git a/copilot/examples/StructsUpdateField.hs b/copilot/examples/StructsUpdateField.hs new file mode 100644 index 000000000..33f5d319f --- /dev/null +++ b/copilot/examples/StructsUpdateField.hs @@ -0,0 +1,159 @@ +-- | An example showing how specifications involving structs (in particular, +-- nested structs) are interpreted and how they are compiled to C using +-- copilot-c99. + +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Main where + +import qualified Prelude as P +import Control.Monad (void, forM_) +import Data.Proxy (Proxy(..)) +import Data.Type.Equality (TestEquality(..), (:~:)(..)) +import GHC.TypeLits (sameSymbol) + +import Language.Copilot +import Copilot.Compile.C99 + +-- | Definition for `Volts`. +data Volts = Volts + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + +-- | `Struct` instance for `Volts`. +instance Struct Volts where + typeName _ = "volts" + toValues volts = [ Value Word16 (numVolts volts) + , Value Bool (flag volts) + ] + -- In order to run struct updates (as used in the "equalityStructUpdate" + -- trigger below) in the Copilot interpreter, we must implement the + -- `updateField` method. To do so, we must check to see if the supplied + -- `Value` has a `Field` with the same name and type as a field in `Volts`. + updateField volts (Value fieldTy (field :: Field fieldName a)) + -- For each field in `Volts`, we must: + -- + -- 1. Check that the field names match using `sameSymbol`. Here, + -- "numVolts" is the expected name, and `fieldName` is the actual name + -- that is supplied as an argument to `updateField`. If the check + -- succeeds, then the `sameSymbol` function will return `Just p`, where + -- `p` is proof that the two names are the same. + | Just Refl <- sameSymbol (Proxy @"numVolts") (Proxy @fieldName) + -- 2. Check that the field types match using `testEquality`. Here, + -- `Word16` is the expected type, and `fieldTy` is the actual type that + -- is supplied as an argument. Again, `testEquality` will return `Just + -- p` (where `p` is a proof) if the two are the same. + , Just Refl <- testEquality Word16 fieldTy + -- 3. If both of the checks above succeed, then we can update the field's + -- value using a record update. + = volts { numVolts = field } + + -- It is possible that the `Value` passed as an argument could correspond + -- to any of the fields in `Volts`, so we must repeat this process for + -- the `flag` field as well. + | Just Refl <- sameSymbol (Proxy @fieldName) (Proxy @"flag") + , Just Refl <- testEquality fieldTy Bool + = volts { flag = field } + + -- If the supplied `Value` does not correspond to any field in `Volts`, + -- then something went wrong in the Copilot interpreter. This case reports + -- this as an error. + | otherwise + = error $ "Unexpected field: " P.++ show field + +-- | `Volts` instance for `Typed`. +instance Typed Volts where + typeOf = Struct (Volts (Field 0) (Field False)) + +data Battery = Battery + { temp :: Field "temp" Word16 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + +-- | `Battery` instance for `Struct`. +instance Struct Battery where + typeName _ = "battery" + toValues battery = [ Value typeOf (temp battery) + , Value typeOf (volts battery) + , Value typeOf (other battery) + ] + -- We implement `updateField` similarly to how we implement it in the + -- `Struct Volts` instance above. + updateField battery (Value fieldTy (field :: Field fieldName a)) + | Just Refl <- sameSymbol (Proxy @fieldName) (Proxy @"temp") + , Just Refl <- testEquality fieldTy Word16 + = battery { temp = field } + + | Just Refl <- sameSymbol (Proxy @fieldName) (Proxy @"volts") + -- Note that writing out the full `Type` for `Volts` is somewhat verbose, + -- so we make use of the `Typed Volts` instance and write `typeOf @Volts` + -- instead. + , Just Refl <- testEquality fieldTy (Array @10 (typeOf @Volts)) + = battery { volts = field } + + | Just Refl <- sameSymbol (Proxy @fieldName) (Proxy @"other") + , Just Refl <- testEquality fieldTy (Array @10 (Array @5 Word32)) + = battery { other = field } + + | otherwise + = error $ "Unexpected field: " P.++ show field + +-- | `Battery` instance for `Typed`. Note that `undefined` is used as an +-- argument to `Field`. This argument is never used, so `undefined` will never +-- throw an error. +instance Typed Battery where + typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + +spec :: Spec +spec = do + let voltsValue :: Volts + voltsValue = + Volts + { numVolts = Field 42 + , flag = Field True + } + + batteryValue :: Battery + batteryValue = + Battery + { temp = Field 0 + , volts = Field (array (replicate 10 voltsValue)) + , other = Field (array (replicate 10 (array (replicate 5 0)))) + } + + battery :: Stream Battery + battery = extern "battery" (Just [batteryValue]) + + -- Check equality, indexing into nested structs and arrays. Note that this is + -- trivial by equality. + trigger "equalitySameIndex" + ((((battery#volts) ! 0)#numVolts) == (((battery#volts) ! 0)#numVolts)) + [arg battery] + + -- Same as previous example, but get a different array index (so should be + -- false). + trigger "equalityDifferentIndices" + ((((battery#other) ! 2) ! 3) == (((battery#other) ! 2) ! 4)) + [arg battery] + + -- Update a struct field, then check it for equality. + let batteryTemp1, batteryTemp2 :: Stream Word16 + batteryTemp1 = (battery ## temp =$ (+1))#temp + batteryTemp2 = battery#temp + 1 + trigger "equalityStructUpdate" + (batteryTemp1 == batteryTemp2) + [arg battery, arg batteryTemp1, arg batteryTemp2] + +main :: IO () +main = do + -- Run the specification using the Copilot interpreter. + interpret 1 spec + + -- Compile the specification to C. + spec' <- reify spec + compile "structs" spec' diff --git a/copilot/examples/what4/Structs.hs b/copilot/examples/what4/Structs.hs index 775572547..6b53d1098 100644 --- a/copilot/examples/what4/Structs.hs +++ b/copilot/examples/what4/Structs.hs @@ -24,6 +24,11 @@ instance Struct Volts where toValues volts = [ Value Word16 (numVolts volts) , Value Bool (flag volts) ] + -- Note that we do not implement `updateField` here. `updateField` is only + -- needed to make updates to structs work in the Copilot interpreter, and we + -- do not use the interpreter in this example. (See + -- `examples/StructsUpdateField.hs` for an example that does implement + -- `updateField`.) -- | `Volts` instance for `Typed`. instance Typed Volts where @@ -42,6 +47,8 @@ instance Struct Battery where , Value typeOf (volts battery) , Value typeOf (other battery) ] + -- Note that we do not implement `updateField` here for the same reasons as in + -- the `Struct Volts` instance above. -- | `Battery` instance for `Typed`. Note that `undefined` is used as an -- argument to `Field`. This argument is never used, so `undefined` will never From 93c3520ef32ff83b0831319f6d1b42bc5ed2bd92 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 30 Sep 2024 15:37:01 -0400 Subject: [PATCH 074/483] copilot-core: Add Haddocks for updateField. Refs #525. This adds Haddocks to the `updateField` that briefly describes how one might implement `updateField` in custom `Struct` instances. Because the full details of implementing `updateField` are somewhat intricate, these Haddocks refer to the `copilot/examples/StructsUpdateField.hs` example for further information. --- copilot-core/src/Copilot/Core/Type.hs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 5710465f3..34cdb4598 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -60,6 +60,25 @@ class Struct a where -- | Transforms all the struct's fields into a list of values. toValues :: a -> [Value a] + -- | Update the value of a struct field. This is only used by the Copilot + -- interpreter, so if you do not plan to use the interpreter, it is safe to + -- omit an implementation of this method. + -- + -- In order to implement 'updateField', you should do the following for each + -- 'Field' in a struct: + -- + -- 1. Check that the name of the 'Field' matches the name of the supplied + -- 'Value' (using 'GHC.TypeLits.sameSymbol'). + -- + -- 2. Check that the type of the 'Field' matches the 'Type' of the supplied + -- 'Value' (using 'DE.testEquality'). + -- + -- 3. If both (1) and (2) succeed, update the corresponding struct field using + -- a record update. + -- + -- For a complete end-to-end example that demonstrates how to implement + -- 'updateField' and use it in the Copilot interpreter, see the + -- @examples/StructsUpdateField.hs@ example in the @copilot@ library. updateField :: a -> Value t -> a updateField = error "Field updates not supported for this type." From b80e9478b51e5bfb5bda147fb788532e218a617d Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 18 Oct 2024 20:10:00 -0400 Subject: [PATCH 075/483] copilot-core: Make error message for unimplemented updateField more informative. Refs #525. Lacking a manual implementation, `updateField`'s default implementation is to throw an error message. This commit adds a suggestion to the user in the error message that the user should manually implement `updateField` in order to fix the error. --- copilot-core/src/Copilot/Core/Type.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 34cdb4598..9e5bfddbc 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -80,7 +80,10 @@ class Struct a where -- 'updateField' and use it in the Copilot interpreter, see the -- @examples/StructsUpdateField.hs@ example in the @copilot@ library. updateField :: a -> Value t -> a - updateField = error "Field updates not supported for this type." + updateField = error $ unlines + [ "Field updates not supported for this type." + , "(Perhaps you need to implement 'updateField' for a 'Struct' instance?)" + ] -- | The field of a struct, together with a representation of its type. data Value a = From 7d9d8184b1a161d382cbccf57a225cc905d2e50b Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 30 Sep 2024 15:39:42 -0400 Subject: [PATCH 076/483] copilot: Document changes in CHANGELOG. Refs #525. --- copilot/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index fc36b32d4..f1a66e960 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -2,6 +2,7 @@ * Update contribution guidelines (#476). * Update README with missing publications. (#544) * Make the what4-propositional example's comments match results. (#535) + * Add example describing how to implement updateField. (#525) 2024-09-07 * Version bump (4.0). (#532) From 75a3d9045dd6da368a2105ab8686129f71577075 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 30 Sep 2024 15:39:50 -0400 Subject: [PATCH 077/483] copilot-core: Document changes in CHANGELOG. Refs #525. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index db1a795be..dacde61da 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-18 + * Add Haddocks for updateField. (#525) + 2024-09-07 * Version bump (4.0). (#532) * Update Op3, Array to support array updates. (#36) From 713833c2f1ea10d885a4306fbf6afbea2f68c587 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 17:59:50 +0000 Subject: [PATCH 078/483] copilot-c99: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot-c99/CHANGELOG | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 406051dd8..59946e6ac 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -18,7 +18,7 @@ 2024-01-07 * Version bump (3.18). (#487) * Change return type of main generated for tests. (#468) - * Print constants in tests using portable suffixes. (#471). + * Print constants in tests using portable suffixes. (#471) * Pass output arrays as arguments to trigger argument functions. (#431) * Compliance with MISRA C 2023 / MISRA C 2012. (#472) @@ -112,30 +112,30 @@ 2020-12-06 * Version bump (3.2). - * Implemented arrays in test driver (#176). - * Fixed nested array initialisation bug (#173). - * Fixed length of buffer allocation for n-dimensional arrays (#174). - * Fixed printing of long ints in test suite (#177). - * Fixed printing of unsigned ints in test suite (#177). - * Fixed '-Wsequence-point' warnings from GCC (#179). - * Split Property.hs (#180). - * Removed 'Test' from module paths (#181). - * Made compiletest take compiler options as an argument (#182). - * Fixed problem with property and empty string in driver CSV (#183). - * Added comma to output of driver to match the interpreter (#184). - * Implemented basic quickcheck based testing (#185). + * Implemented arrays in test driver. (#176) + * Fixed nested array initialisation bug. (#173) + * Fixed length of buffer allocation for n-dimensional arrays. (#174) + * Fixed printing of long ints in test suite. (#177) + * Fixed printing of unsigned ints in test suite. (#177) + * Fixed '-Wsequence-point' warnings from GCC. (#179) + * Split Property.hs. (#180) + * Removed 'Test' from module paths. (#181) + * Made compiletest take compiler options as an argument. (#182) + * Fixed problem with property and empty string in driver CSV. (#183) + * Added comma to output of driver to match the interpreter. (#184) + * Implemented basic quickcheck based testing. (#185) 2020-03-30 - * Version bump (3.1.2) + * Version bump (3.1.2). * Fixed bug where stream buffers are updated too soon. (#188) * Updated description of cabal package. (#192) 2019-12-23 * Version bump (3.1.1). - * Fixed bug with constant structs and arrays.(#200). + * Fixed bug with constant structs and arrays. (#200) 2019-11-22 * Version bump (3.1). - * Remove ExternFun (#207). - * Fix bug in code generation for local expression (#198). - * Implement code generation for labels (trivially) (#199). + * Remove ExternFun. (#207) + * Fix bug in code generation for local expression. (#198) + * Implement code generation for labels (trivially). (#199) From 7a7764633061bc89fe245456c209fab6593820a7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:01:04 +0000 Subject: [PATCH 079/483] copilot-core: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot-core/CHANGELOG | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index dacde61da..872fbecc9 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -141,13 +141,13 @@ 2020-12-06 Ivan Perez * Version bump (3.2). - * Fixed implementation of tysize for n-dimensional arrays. (#147). - * Removed sorting of interpreter output (#148). - * Minor documentation fixes (#149, #151). + * Fixed implementation of tysize for n-dimensional arrays. (#147) + * Removed sorting of interpreter output. (#148) + * Minor documentation fixes. (#149, #151) * Credits: @fdedden. 2019-11-22 Ivan Perez * Version bump (3.1). - * Eliminate random modules and generators (#157). - * Updated contact information for 'impossible' error (#154). - * Implement missing pretty printer for Index operator (#155). + * Eliminate random modules and generators. (#157) + * Updated contact information for 'impossible' error. (#154) + * Implement missing pretty printer for Index operator. (#155) From 7db6774a907b197a9d88c74bd460c1c7bfdf9285 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:01:17 +0000 Subject: [PATCH 080/483] copilot-language: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot-language/CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 73789c0c6..175ac6981 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -112,11 +112,11 @@ 2020-05-07 * Version bump (3.2). - * Fixed the reverse order of triggers (#114). - * Update description, bug-reports, changelog fields in cabal file - (#116). - * Bump ghc-prim version bounds (#122). + * Fixed the reverse order of triggers. (#114) + * Update description, bug-reports, changelog fields in cabal file. + (#116) + * Bump ghc-prim version bounds. (#122) 2019-11-22 Ivan Perez * Version bump (3.1). - * Remove ExternFun (#118). + * Remove ExternFun. (#118) From ac701e5b8580f568b993c1b5303305a57c3ba735 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:01:33 +0000 Subject: [PATCH 081/483] copilot-libraries: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot-libraries/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 9bdfd287a..672949f3d 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -88,7 +88,7 @@ 2020-12-06 * Version bump (3.2). - * Update description, bug-reports, homepage fields in cabal file (#129). + * Update description, bug-reports, homepage fields in cabal file. (#129) 2019-11-22 Ivan Perez * Version bump (3.1). From c97ef5720b112838357392c5536fda7ccedfaf54 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:01:44 +0000 Subject: [PATCH 082/483] copilot-theorem: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot-theorem/CHANGELOG | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index fa1870c7f..31bdf9222 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -89,18 +89,18 @@ * Version bump (3.4). (#231) 2021-05-07 - * Version bump (3.3). (#217). - * Adjust contraints on version of what4. (#90). + * Version bump (3.3). (#217) + * Adjust contraints on version of what4. (#90) 2021-03-07 - * Version bump (3.2.1). (#92). - * Completed the documentation. (#95, #93). + * Version bump (3.2.1). (#92) + * Completed the documentation. (#95, #93) 2020-12-06 * Version bump (3.2). - * Update description, bug-reports and homepage field in cabal file - (#97). + * Update description, bug-reports and homepage field in cabal file. + (#97) 2019-11-22 Ivan Perez * Version bump (3.1). - * Remove ExternFun (#99). + * Remove ExternFun. (#99) From 20926fa582ed2f359340227fbeb2b541948a6aac Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:01:54 +0000 Subject: [PATCH 083/483] copilot: Standardize issue references in changelog. Refs #550. The CHANGELOGs contain lines with irregular formatting in the way that issue numbers are referenced: sometimes a period goes before, sometimes it's added after the issue number, sometimes both. This commit puts the period always before the issue number. --- copilot/CHANGELOG | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index f1a66e960..adfa10162 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,5 @@ 2024-10-18 - * Update contribution guidelines (#476). + * Update contribution guidelines. (#476) * Update README with missing publications. (#544) * Make the what4-propositional example's comments match results. (#535) * Add example describing how to implement updateField. (#525) @@ -123,11 +123,11 @@ 2020-12-06 * Update optparse-applicative dependency version for newer base - versions. (#61). - * Add Ivan Perez as co-maintainer (#51). - * Update description in cabal file to match copilot-core (#50). + versions. (#61) + * Add Ivan Perez as co-maintainer. (#51) + * Update description in cabal file to match copilot-core. (#50) 2019-11-22 Ivan Perez * Version bump (3.1). - * Update multiple examples (#41). - * Update instructions to match new repositry name (#45). + * Update multiple examples. (#41) + * Update instructions to match new repositry name. (#45) From b7be4f0472c410af2d04365d6047d7461796229b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:02 +0000 Subject: [PATCH 084/483] copilot-c99: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot-c99/CHANGELOG | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 59946e6ac..6ce08a92a 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -111,7 +111,7 @@ * Completed the documentation. (#171) 2020-12-06 - * Version bump (3.2). + * Version bump (3.2). (#65) * Implemented arrays in test driver. (#176) * Fixed nested array initialisation bug. (#173) * Fixed length of buffer allocation for n-dimensional arrays. (#174) @@ -126,16 +126,16 @@ * Implemented basic quickcheck based testing. (#185) 2020-03-30 - * Version bump (3.1.2). + * Version bump (3.1.2). (#187) * Fixed bug where stream buffers are updated too soon. (#188) * Updated description of cabal package. (#192) 2019-12-23 - * Version bump (3.1.1). + * Version bump (3.1.1). (#191) * Fixed bug with constant structs and arrays. (#200) 2019-11-22 - * Version bump (3.1). + * Version bump (3.1). (#46) * Remove ExternFun. (#207) * Fix bug in code generation for local expression. (#198) * Implement code generation for labels (trivially). (#199) From 36a92ef5ead30d37b44949aec53da3c6455e30c4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:20 +0000 Subject: [PATCH 085/483] copilot-core: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot-core/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 872fbecc9..fa9caba6f 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -140,14 +140,14 @@ * Completed the documentation. (#145) 2020-12-06 Ivan Perez - * Version bump (3.2). + * Version bump (3.2). (#65) * Fixed implementation of tysize for n-dimensional arrays. (#147) * Removed sorting of interpreter output. (#148) * Minor documentation fixes. (#149, #151) * Credits: @fdedden. 2019-11-22 Ivan Perez - * Version bump (3.1). + * Version bump (3.1). (#46) * Eliminate random modules and generators. (#157) * Updated contact information for 'impossible' error. (#154) * Implement missing pretty printer for Index operator. (#155) From 45deee9b69af1549b39f9b51c1bf5705615e74a1 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:26 +0000 Subject: [PATCH 086/483] copilot-language: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot-language/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 175ac6981..08c167726 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -111,12 +111,12 @@ * Completed the documentation. (#112) 2020-05-07 - * Version bump (3.2). + * Version bump (3.2). (#65) * Fixed the reverse order of triggers. (#114) * Update description, bug-reports, changelog fields in cabal file. (#116) * Bump ghc-prim version bounds. (#122) 2019-11-22 Ivan Perez - * Version bump (3.1). + * Version bump (3.1). (#46) * Remove ExternFun. (#118) From 186a9fbd3b92832c87693fdc54e02bb1726a25b4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:34 +0000 Subject: [PATCH 087/483] copilot-libraries: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot-libraries/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 672949f3d..789db4a3b 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -87,8 +87,8 @@ * Completed the documentation. (#127) 2020-12-06 - * Version bump (3.2). + * Version bump (3.2). (#65) * Update description, bug-reports, homepage fields in cabal file. (#129) 2019-11-22 Ivan Perez - * Version bump (3.1). + * Version bump (3.1). (#46) From 827d41072a3ce8dafd85b73adda4f127e2c15013 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:40 +0000 Subject: [PATCH 088/483] copilot-theorem: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot-theorem/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 31bdf9222..be572ced5 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -97,10 +97,10 @@ * Completed the documentation. (#95, #93) 2020-12-06 - * Version bump (3.2). + * Version bump (3.2). (#65) * Update description, bug-reports and homepage field in cabal file. (#97) 2019-11-22 Ivan Perez - * Version bump (3.1). + * Version bump (3.1). (#46) * Remove ExternFun. (#99) From c95753400c03cc5d103ec500aa5a85c5fb8e0ab8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:07:48 +0000 Subject: [PATCH 089/483] copilot: Add missing issue number. Refs #550. The standard format in the changelog is that entries should include the issue number that documents what led to the change. This commit adds missing issue numbers for known issues. --- copilot/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index adfa10162..af2860daf 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -128,6 +128,6 @@ * Update description in cabal file to match copilot-core. (#50) 2019-11-22 Ivan Perez - * Version bump (3.1). + * Version bump (3.1). (#46) * Update multiple examples. (#41) * Update instructions to match new repositry name. (#45) From ca4f6d12f5cc064a86797f43c8cae8b1805cc6b7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:16:04 +0000 Subject: [PATCH 090/483] copilot-core: Remove maintainer from entry. Refs #550. Blocks of entries in the changelog do not have the maintainer next to them anymore. However, two of the entries uses an old format where the maintainer was listed next to the date. This commit removes that maintainer, to keep all blocks following a standard format. --- copilot-core/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index fa9caba6f..3f21f08c8 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -139,14 +139,14 @@ * Version bump (3.2.1). (#136) * Completed the documentation. (#145) -2020-12-06 Ivan Perez +2020-12-06 * Version bump (3.2). (#65) * Fixed implementation of tysize for n-dimensional arrays. (#147) * Removed sorting of interpreter output. (#148) * Minor documentation fixes. (#149, #151) * Credits: @fdedden. -2019-11-22 Ivan Perez +2019-11-22 * Version bump (3.1). (#46) * Eliminate random modules and generators. (#157) * Updated contact information for 'impossible' error. (#154) From 22ca316dd6732ea133d173d17c7e3f1257093f07 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:16:16 +0000 Subject: [PATCH 091/483] copilot-language: Remove maintainer from entry. Refs #550. Blocks of entries in the changelog do not have the maintainer next to them anymore. However, one of the entries uses an old format where the maintainer was listed next to the date. This commit removes that maintainer, to keep all blocks following a standard format. --- copilot-language/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 08c167726..dac6be0d3 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -117,6 +117,6 @@ (#116) * Bump ghc-prim version bounds. (#122) -2019-11-22 Ivan Perez +2019-11-22 * Version bump (3.1). (#46) * Remove ExternFun. (#118) From e6ef7adc8449826a3bdef46cba4cb83e73d79e6b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:16:21 +0000 Subject: [PATCH 092/483] copilot-libraries: Remove maintainer from entry. Refs #550. Blocks of entries in the changelog do not have the maintainer next to them anymore. However, one of the entries uses an old format where the maintainer was listed next to the date. This commit removes that maintainer, to keep all blocks following a standard format. --- copilot-libraries/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 789db4a3b..75d5360ba 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -90,5 +90,5 @@ * Version bump (3.2). (#65) * Update description, bug-reports, homepage fields in cabal file. (#129) -2019-11-22 Ivan Perez +2019-11-22 * Version bump (3.1). (#46) From b1b21965afd2aca79432662cef1f5a1588e0e22c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:16:27 +0000 Subject: [PATCH 093/483] copilot-theorem: Remove maintainer from entry. Refs #550. Blocks of entries in the changelog do not have the maintainer next to them anymore. However, one of the entries uses an old format where the maintainer was listed next to the date. This commit removes that maintainer, to keep all blocks following a standard format. --- copilot-theorem/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index be572ced5..ae70b7be8 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -101,6 +101,6 @@ * Update description, bug-reports and homepage field in cabal file. (#97) -2019-11-22 Ivan Perez +2019-11-22 * Version bump (3.1). (#46) * Remove ExternFun. (#99) From 47a8bf90ffabc7d72a81c4ec9219df306ff51593 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 20:16:32 +0000 Subject: [PATCH 094/483] copilot: Remove maintainer from entry. Refs #550. Blocks of entries in the changelog do not have the maintainer next to them anymore. However, one of the entries uses an old format where the maintainer was listed next to the date. This commit removes that maintainer, to keep all blocks following a standard format. --- copilot/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index af2860daf..40d605645 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -127,7 +127,7 @@ * Add Ivan Perez as co-maintainer. (#51) * Update description in cabal file to match copilot-core. (#50) -2019-11-22 Ivan Perez +2019-11-22 * Version bump (3.1). (#46) * Update multiple examples. (#41) * Update instructions to match new repositry name. (#45) From 7332215c4325ad45c5fec7d2230734d98e726276 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:03:33 +0000 Subject: [PATCH 095/483] copilot-c99: Document changes in CHANGELOG. Refs #550. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 6ce08a92a..f8dd97230 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-19 + * Standardize changelog format. (#550) + 2024-09-07 * Version bump (4.0). (#532) * Add support for array updates. (#36) From 980cc5f044525266e572b30512e218bea3e2ab2d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:03:59 +0000 Subject: [PATCH 096/483] copilot-core: Document changes in CHANGELOG. Refs #550. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 3f21f08c8..d93518477 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,5 +1,6 @@ -2024-10-18 +2024-10-19 * Add Haddocks for updateField. (#525) + * Standardize changelog format. (#550) 2024-09-07 * Version bump (4.0). (#532) From fe188d1923b5c3b8e751060d2c3a5507c939c074 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:04:34 +0000 Subject: [PATCH 097/483] copilot-language: Document changes in CHANGELOG. Refs #550. --- copilot-language/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index dac6be0d3..0712d4cc6 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,5 +1,6 @@ -2024-10-15 +2024-10-19 * Reject duplicate externs in properties and theorems. (#536) + * Standardize changelog format. (#550) 2024-09-07 * Version bump (4.0). (#532) From a1010aeca66b8a21e7b5366fc560bfbe37da1713 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:04:56 +0000 Subject: [PATCH 098/483] copilot-libraries: Document changes in CHANGELOG. Refs #550. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 75d5360ba..6c045c332 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-19 + * Standardize changelog format. (#550) + 2024-09-07 * Version bump (4.0). (#532) * Rename operator to avoid name clash. (#36) From 6edc8b5a5d8be8be3616d9b38a8ad622da9dae01 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:05:25 +0000 Subject: [PATCH 099/483] copilot-theorem: Document changes in CHANGELOG. Refs #550. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index ae70b7be8..67dabaacd 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2024-10-19 + * Standardize changelog format. (#550) + 2024-09-07 * Version bump (4.0). (#532) * Add support for struct updates in Copilot.Theorem.What4. (#524) From 578eff68a4a59a4e99b3179fbf5d47e97881ee07 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 19 Oct 2024 18:06:51 +0000 Subject: [PATCH 100/483] copilot: Document changes in CHANGELOG. Refs #550. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 40d605645..72808ad85 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,8 +1,9 @@ -2024-10-18 +2024-10-19 * Update contribution guidelines. (#476) * Update README with missing publications. (#544) * Make the what4-propositional example's comments match results. (#535) * Add example describing how to implement updateField. (#525) + * Standardize changelog format. (#550) 2024-09-07 * Version bump (4.0). (#532) From 2fbe5d285d5319faa50d7110d2081e5a6b6fec15 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Mon, 14 Oct 2024 14:30:24 +0200 Subject: [PATCH 101/483] copilot: Add installation instructions for Fedora 40 and up. Refs #542. Since September 2024, Copilot is available in Fedora 40 and newer. This commit adds installation instruction for Fedora. --- copilot/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/copilot/README.md b/copilot/README.md index 870cca956..4fb3180d4 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -76,6 +76,25 @@ It should end with a line like the following and not print any error messages: ghci> ghci> Leaving GHCi. ``` +### Fedora 40 + +On Fedora 40 or newer, Copilot can be installed directly from the package +repositories with: + +```sh +$ sudo dnf install ghc-copilot-devel +``` + +To test that Copilot is available, execute the following: +```sh +$ ghci <<< 'import Language.Copilot' +``` + +It should end with a line like the following and not print any error messages: +```sh +ghci> ghci> Leaving GHCi. +``` + ### Other Linux distributions On other Linux distributions or older Debian-based distributions, to use From ff796834052bba925bbc968454ab2d0f03d0f347 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Tue, 22 Oct 2024 12:28:44 +0200 Subject: [PATCH 102/483] copilot: Document changes in CHANGELOG. Refs #542. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 72808ad85..6d7fb9472 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,9 +1,10 @@ -2024-10-19 +2024-10-22 * Update contribution guidelines. (#476) * Update README with missing publications. (#544) * Make the what4-propositional example's comments match results. (#535) * Add example describing how to implement updateField. (#525) * Standardize changelog format. (#550) + * Add installation instructions for Fedora 40 and up. (#542) 2024-09-07 * Version bump (4.0). (#532) From 7de5f4a0734749e84f128ecbe134856495d4248b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 28 Oct 2024 20:07:52 +0000 Subject: [PATCH 103/483] copilot-core: Deprecate Copilot.Core.Type.UType.uTypeType. Refs #484. The record field uTypeType, part of Copilot.Core.Type.UType, is never really used within the same module or in any other part of Copilot or, as far as we know, by any user of Copilot. Also, we cannot use it to obtain the value inside the UType due to it being an existential type. It also makes the test report lower coverage than they otherwise might be, since the record access function is not and cannot be used by the tests. This field is not a necessary part of Copilot's interface, and not required by Copilot's requirements. Therefore, it would be suitable to remove it from the module. This commit deprecates the field uTypeType, so that it can be removed in a future version of Copilot. --- copilot-core/src/Copilot/Core/Type.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 9e5bfddbc..b31a35094 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -268,6 +268,7 @@ instance (Typeable t, Typed t, KnownNat n) => Typed (Array n t) where -- | A untyped type (no phantom type). data UType = forall a . Typeable a => UType { uTypeType :: Type a } +{-# DEPRECATED uTypeType "This field is deprecated in Copilot 4.1. Use pattern matching instead." #-} instance Eq UType where UType ty1 == UType ty2 = typeRep ty1 == typeRep ty2 From 2a476637e44df5b1c125fa5c270c300479e0df72 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 28 Oct 2024 20:10:01 +0000 Subject: [PATCH 104/483] copilot-core: Document changes in CHANGELOG. Refs #484. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index d93518477..867d513c8 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,6 +1,7 @@ -2024-10-19 +2024-10-28 * Add Haddocks for updateField. (#525) * Standardize changelog format. (#550) + * Deprecate Copilot.Core.Type.UType.uTypeType. (#484) 2024-09-07 * Version bump (4.0). (#532) From fb38f274ff54c0cf3da9d72ecde10068cc121384 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 8 Nov 2024 09:02:17 +0000 Subject: [PATCH 105/483] Bump versions and constraints. Refs #561. --- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 4 ++-- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot/copilot.cabal | 14 +++++++------- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 2acefad8c..f6a196b81 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.0 +version : 4.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.0 && < 4.1 + , copilot-core >= 4.1 && < 4.2 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 500c97df8..232145325 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.0 +version: 4.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 9ad90e75e..87e668aad 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.0 +version: 4.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.0 && < 4.1 + copilot-core >= 4.1 && < 4.2 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index a9b79547f..ad0ec0725 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.0 +version: 4.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.0 && < 4.1 - , copilot-interpreter >= 4.0 && < 4.1 - , copilot-theorem >= 4.0 && < 4.1 + , copilot-core >= 4.1 && < 4.2 + , copilot-interpreter >= 4.1 && < 4.2 + , copilot-theorem >= 4.1 && < 4.2 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 741a4cc8e..fc312c91d 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.0 +version: 4.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.7 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.0 && < 4.1 + , copilot-language >= 4.1 && < 4.2 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 73746675a..3d3371335 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.0 +version: 4.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.0 && < 4.1 + copilot-core >= 4.1 && < 4.2 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 46416e9ee..9d7dd0fda 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.0 +version : 4.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 4.0 && < 4.1 - , copilot-prettyprinter >= 4.0 && < 4.1 + , copilot-core >= 4.1 && < 4.2 + , copilot-prettyprinter >= 4.1 && < 4.2 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 467ae50c9..c8383ae9a 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.0 +version: 4.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.5 - , copilot-core >= 4.0 && < 4.1 - , copilot-theorem >= 4.0 && < 4.1 - , copilot-language >= 4.0 && < 4.1 - , copilot-libraries >= 4.0 && < 4.1 - , copilot-c99 >= 4.0 && < 4.1 - , copilot-prettyprinter >= 4.0 && < 4.1 + , copilot-core >= 4.1 && < 4.2 + , copilot-theorem >= 4.1 && < 4.2 + , copilot-language >= 4.1 && < 4.2 + , copilot-libraries >= 4.1 && < 4.2 + , copilot-c99 >= 4.1 && < 4.2 + , copilot-prettyprinter >= 4.1 && < 4.2 exposed-modules: Language.Copilot, Language.Copilot.Main From 22aec9c96c9a9a0fadc216daa8d88df760397dd4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 8 Nov 2024 09:06:11 +0000 Subject: [PATCH 106/483] Document changes in CHANGELOG. Refs #561. --- copilot-c99/CHANGELOG | 3 ++- copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 3 ++- copilot-libraries/CHANGELOG | 3 ++- copilot-prettyprinter/CHANGELOG | 3 +++ copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 18 insertions(+), 6 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index f8dd97230..1c045c218 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-19 +2024-11-07 + * Version bump (4.1). (#561) * Standardize changelog format. (#550) 2024-09-07 diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 867d513c8..7ca104937 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-28 +2024-11-07 + * Version bump (4.1). (#561) * Add Haddocks for updateField. (#525) * Standardize changelog format. (#550) * Deprecate Copilot.Core.Type.UType.uTypeType. (#484) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 5cb1ad93c..bd2aadc09 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-07 + * Version bump (4.1). (#561) + 2024-09-07 * Version bump (4.0). (#532) * Add support for array updates. (#36) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 0712d4cc6..1d205da0e 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-19 +2024-11-07 + * Version bump (4.1). (#561) * Reject duplicate externs in properties and theorems. (#536) * Standardize changelog format. (#550) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 6c045c332..c4ce6ddcf 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-19 +2024-11-07 + * Version bump (4.1). (#561) * Standardize changelog format. (#550) 2024-09-07 diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index e5409a927..ba157f37b 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-07 + * Version bump (4.1). (#561) + 2024-09-07 * Version bump (4.0). (#532) * Add support for pretty-printing struct update expressions. (#526) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 67dabaacd..09c28ca7e 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-19 +2024-11-07 + * Version bump (4.1). (#561) * Standardize changelog format. (#550) 2024-09-07 diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 6d7fb9472..ce4a1c37a 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2024-10-22 +2024-11-07 + * Version bump (4.1). (#561) * Update contribution guidelines. (#476) * Update README with missing publications. (#544) * Make the what4-propositional example's comments match results. (#535) From 8ec934906599c19591c4c010b87b15964baa73ad Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 17:58:44 -0800 Subject: [PATCH 107/483] copilot-c99: Remove uses of Copilot.Core.Expr.UExpr.uExprType. Refs #565. The record fields uExprType and uExprExpr, part of Copilot.Core.Expr.UExpr, are never really used within the module and only rarely used elsewhere in Copilot or, as far as we know, by any user of Copilot. As a matter of fact, we cannot use them to obtain the value inside the UExpr due to the existential it encloses. These fields are not necessary parts of the interface to use Copilot, and not required by Copilot's requirements. Therefore, it would be suitable to remove that definitions from the module. This commit removes any uses of uExprType and uExprExpr in copilot-c99 and uses positional arguments instead. --- copilot-c99/src/Copilot/Compile/C99/CodeGen.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs index 3f9693438..e71e7a0df 100644 --- a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs +++ b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs @@ -279,7 +279,7 @@ mkStep cSettings streams triggers exts = aTmpDeclns = zipWith declare args aTempNames where declare :: UExpr -> C.Ident -> C.Decln - declare (UExpr { uExprType = ty }) tmpVar = + declare (UExpr ty _) tmpVar = C.VarDecln Nothing (transType ty) tmpVar Nothing triggerCheckStmt :: C.Stmt @@ -301,7 +301,7 @@ mkStep cSettings streams triggers exts = argAssigns = zipWith3 assign aTempNames aArgNames args assign :: C.Ident -> C.Ident -> UExpr -> C.Expr - assign aTempName aArgName (UExpr { uExprType = ty }) = + assign aTempName aArgName (UExpr ty _) = updateVar aTempName aArgName ty aArgNames :: [C.Ident] @@ -314,7 +314,7 @@ mkStep cSettings streams triggers exts = -- so we also need the type of the expression, which is enclosed -- in the second argument, an UExpr. passArg :: String -> UExpr -> C.Expr - passArg aTempName (UExpr { uExprType = ty }) = + passArg aTempName (UExpr ty _) = case ty of -- Special case for Struct to pass reference to temporary -- struct variable to handler. (See the comments for From ec7899a3867dda2c52acca0d90bcac231d2e1fe2 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:20:55 -0800 Subject: [PATCH 108/483] copilot-prettyprinter: Remove uses of Copilot.Core.Expr.UExpr.uExprExpr. Refs #565. The record fields uExprType and uExprExpr, part of Copilot.Core.Expr.UExpr, are never really used within the module and only rarely used elsewhere in Copilot or, as far as we know, by any user of Copilot. As a matter of fact, we cannot use them to obtain the value inside the UExpr due to the existential it encloses. These fields are not necessary parts of the interface to use Copilot, and not required by Copilot's requirements. Therefore, it would be suitable to remove that definitions from the module. This commit removes any uses of uExprExpr in copilot-prettyprinter and uses positional arguments instead. --- copilot-prettyprinter/src/Copilot/PrettyPrint.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs index 64ca9c90a..50be4466f 100644 --- a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs +++ b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs @@ -43,7 +43,7 @@ ppExpr e0 = case e0 of -- -- The type is ignored, and only the expression is pretty-printed. ppUExpr :: UExpr -> Doc -ppUExpr UExpr { uExprExpr = e0 } = ppExpr e0 +ppUExpr (UExpr _ e0) = ppExpr e0 -- | Pretty-print a unary operation. ppOp1 :: Op1 a b -> Doc -> Doc From 8852cc365e25d8dcad39ec287d4d29906cb598be Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:32:27 -0800 Subject: [PATCH 109/483] copilot-theorem: Remove uses of Copilot.Core.Expr.UExpr.uExprType,uExprExpr. Refs #565. The record fields uExprType and uExprExpr, part of Copilot.Core.Expr.UExpr, are never really used within the module and only rarely used elsewhere in Copilot or, as far as we know, by any user of Copilot. As a matter of fact, we cannot use them to obtain the value inside the UExpr due to the existential it encloses. These fields are not necessary parts of the interface to use Copilot, and not required by Copilot's requirements. Therefore, it would be suitable to remove that definitions from the module. This commit removes any uses of uExprType and uExprExpr in copilot-theorem and uses positional arguments instead. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 6971ae673..960df8d93 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -324,7 +324,7 @@ computeTriggerState sym spec = forM (CS.specTriggers spec) $ args' <- mapM computeArg args return (nm, guard', args') where - computeArg (CE.UExpr { CE.uExprType = tp, CE.uExprExpr = ex }) = do + computeArg (CE.UExpr tp ex) = do v <- translateExpr sym mempty ex (RelativeOffset 0) return (Some tp, v) From af61c373a1a20a8ad29cedb097d9123cd7ea4ca8 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:38:34 -0800 Subject: [PATCH 110/483] copilot-core: Deprecate fields of Copilot.Core.Expr.UExpr. Refs #565. The record fields uExprType and uExprExpr, part of Copilot.Core.Expr.UExpr, are never really used within the module and only rarely used elsewhere in Copilot or, as far as we know, by any user of Copilot. As a matter of fact, we cannot use them to obtain the value inside the UExpr due to the existential it encloses. These fields are not necessary parts of the interface to use Copilot, and not required by Copilot's requirements. Therefore, it would be suitable to remove that definitions from the module. This commit deprecates the fields uExprType and uExprExpr, so that they can be removed in a future version of Copilot. --- copilot-core/src/Copilot/Core/Expr.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-core/src/Copilot/Core/Expr.hs b/copilot-core/src/Copilot/Core/Expr.hs index 618b18fab..277e919d1 100644 --- a/copilot-core/src/Copilot/Core/Expr.hs +++ b/copilot-core/src/Copilot/Core/Expr.hs @@ -71,3 +71,4 @@ data UExpr = forall a . Typeable a => UExpr { uExprType :: Type a , uExprExpr :: Expr a } +{-# DEPRECATED uExprType, uExprExpr "These fields are deprecated in Copilot 4.2. Use pattern matching instead." #-} From 41fa1d2aef42a1abbb18ff578790ad843822e1e3 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:44:54 -0800 Subject: [PATCH 111/483] copilot-c99: Document changes in CHANGELOG. Refs #565. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 1c045c218..50c704f20 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-14 + * Remove uses of Copilot.Core.Expr.UExpr.uExprType. (#565) + 2024-11-07 * Version bump (4.1). (#561) * Standardize changelog format. (#550) From 0bbaac5ab53f39b436d492061ee74e51871a6ca4 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:49:12 -0800 Subject: [PATCH 112/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #565. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index ba157f37b..6ea452f0e 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-15 + * Remove uses of Copilot.Core.Expr.UExpr.uExprExpr. (#565) + 2024-11-07 * Version bump (4.1). (#561) From 0cdc163bca7447b416f18830e43d3a62ffea10b3 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:51:04 -0800 Subject: [PATCH 113/483] copilot-theorem: Document changes in CHANGELOG. Refs #565. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 09c28ca7e..7508a978d 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-15 + * Remove uses of Copilot.Core.Expr.UExpr.uExprType,uExprExpr. (#565) + 2024-11-07 * Version bump (4.1). (#561) * Standardize changelog format. (#550) From 4d8431d77632f4ed91cdab97bcc80f7331ce09cd Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 14 Nov 2024 18:52:31 -0800 Subject: [PATCH 114/483] copilot-core: Document changes in CHANGELOG. Refs #565. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 7ca104937..3770ee808 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2024-11-15 + * Deprecate fields of Copilot.Core.Expr.UExpr. (#565) + 2024-11-07 * Version bump (4.1). (#561) * Add Haddocks for updateField. (#525) From 233b5dbbf4c9f14e1a3c082a99a5fbfcf7aae2da Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 23 Oct 2024 17:55:45 +0000 Subject: [PATCH 115/483] copilot-core: Increase test coverage of Copilot.Core.Type. Refs #555. The test coverage of copilot-core is currently reported at 75% by Hackage. Although there are exceptions to what we can test (record fields of existential types) or should test (proxies, automatically generated constructors and accessor functions), the coverage of our tests should include very top level definition and expression that can be tested without having to modify the implementation of copilot-core itself. This commit increases the test coverage for Copilot.Core.Type by introducing new tests involving structs and arrays. Some of these are hard coded, contrary to the idea of using randomized testing to carry out the tests, because the use of dependent types limits how much can be generated at runtime without substantial and complex testing infrastructure. Proxies are still reported as untested, since doing so would require modifying the code of Copilot itself to introduce strictness. The same applies to functions that do not use some of their arguments (often involving dependent types). --- copilot-core/tests/Test/Copilot/Core/Type.hs | 164 ++++++++++++++++--- 1 file changed, 138 insertions(+), 26 deletions(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type.hs b/copilot-core/tests/Test/Copilot/Core/Type.hs index 602dfa08a..9f9cb6d5e 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type.hs @@ -1,12 +1,19 @@ -{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} -- | Test copilot-core:Copilot.Core.Type. module Test.Copilot.Core.Type where -- External imports import Data.Int (Int16, Int32, Int64, Int8) import Data.Maybe (isJust) -import Data.Type.Equality (testEquality) +import Data.Proxy (Proxy (..)) +import Data.Type.Equality (TestEquality (..), testEquality, + (:~:) (..)) import Data.Word (Word16, Word32, Word64, Word8) +import GHC.TypeLits (sameSymbol) +import Prelude as P import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.QuickCheck (Gen, Property, arbitrary, elements, @@ -50,6 +57,8 @@ tests = testUTypesInequality , testProperty "inequality of utype via typeOf" testUTypesTypeOfInequality + , testProperty "inequality of different types" + testTypesInequality , testProperty "fieldName matches field name (positive)" testFieldNameOk , testProperty "fieldName matches field name (negative)" @@ -58,6 +67,10 @@ tests = testShowField , testProperty "Show struct" testShowStruct + , testProperty "Update struct" + testUpdateStruct + , testProperty "Update struct" + testUpdateStructFail , testProperty "accessorName matches field name (positive)" testAccessorNameOk , testProperty "accessorName matches field name (negative)" @@ -96,7 +109,7 @@ testSimpleTypesInequality = forAllBlind twoDiffTypes $ \(t1, t2) -> , simpleType Float , simpleType Double , simpleType (Array Int8 :: Type (Array 3 Int8)) - , simpleType (Struct (S (Field 0))) + , simpleType (Struct (S (Field 0) (Field 0))) ] -- | Test that the equality relation for simple types is reflexive. @@ -177,12 +190,12 @@ testTypeSize2 = property $ typeSize a == 36 -- | Test that equality is symmetric for UTypes via testEquality. testUTypesEqualitySymmetric :: Property testUTypesEqualitySymmetric = - forAllBlind (elements utypes) $ \(UType t1) -> isJust (testEquality t1 t1) + forAllBlind (elements utypes) $ \(UType t1) -> testEquality t1 t1 == Just Refl -- | Test that testEquality implies equality for UTypes. testUTypesEq :: Property testUTypesEq = - forAllBlind (elements utypes) $ \t@(UType t1) -> isJust (testEquality t1 t1) ==> t == t + forAllBlind (elements utypes) $ \t@(UType t1) -> (testEquality t1 t1 == Just Refl) ==> t == t -- | Test that any two different UTypes are not equal. -- @@ -199,6 +212,21 @@ testUTypesInequality = forAllBlind twoDiffTypes $ \(t1, t2) -> (t1:t2:_) -> return (t1, t2) _ -> return (UType Bool, UType Bool) +-- | Test that any two different Types are not equal. +-- +-- This function pre-selects two Types from a list of different UTypes, which +-- guarantees that they will be different. +testTypesInequality :: Property +testTypesInequality = forAllBlind twoDiffTypes $ \(UType t1, UType t2) -> + testEquality t1 t2 == Nothing + where + twoDiffTypes :: Gen (UType, UType) + twoDiffTypes = do + shuffled <- shuffle utypes + case shuffled of + (t1:t2:_) -> return (t1, t2) + _ -> return (UType Bool, UType Bool) + -- | Different UTypes. utypes :: [UType] utypes = @@ -213,19 +241,43 @@ utypes = , UType Word64 , UType Float , UType Double - , UType a - , UType b + , UType a1 + , UType a2 + , UType a3 + , UType a4 + , UType b1 + , UType b2 + , UType b3 + , UType b4 , UType c ] where - a :: Type (Array 3 Int8) - a = Array Int8 + a1 :: Type (Array 3 Int8) + a1 = Array Int8 + + a2 :: Type (Array 4 Int8) + a2 = Array Int8 + + a3 :: Type (Array 5 Int8) + a3 = Array Int8 + + a4 :: Type (Array 6 Int8) + a4 = Array Int8 - b :: Type (Array 4 Int8) - b = Array Int8 + b1 :: Type (Array 3 Int16) + b1 = Array Int16 + + b2 :: Type (Array 4 Int16) + b2 = Array Int16 + + b3 :: Type (Array 5 Int16) + b3 = Array Int16 + + b4 :: Type (Array 6 Int16) + b4 = Array Int16 c :: Type S - c = Struct (S (Field 0)) + c = Struct (S (Field 0) (Field 0)) -- | Test that any two different UTypes are not equal. -- @@ -259,47 +311,86 @@ uTypesTypeOf = , UType (typeOf :: Type Float) , UType (typeOf :: Type Double) , UType (typeOf :: Type (Array 3 Int8)) + , UType (typeOf :: Type (Array 3 Int16)) + , UType (typeOf :: Type (Array 3 Int32)) + , UType (typeOf :: Type (Array 3 Int64)) + , UType (typeOf :: Type (Array 3 Word8)) + , UType (typeOf :: Type (Array 3 Word16)) + , UType (typeOf :: Type (Array 3 Word32)) + , UType (typeOf :: Type (Array 3 Word64)) + , UType (typeOf :: Type (Array 3 Double)) + , UType (typeOf :: Type (Array 3 Float)) , UType (typeOf :: Type S) ] -- | Test the fieldName function (should succeed). testFieldNameOk :: Property -testFieldNameOk = forAll arbitrary $ \k -> - fieldName (s1 (S (Field k))) == s1FieldName +testFieldNameOk = forAll arbitrary $ \k1 -> + forAll arbitrary $ \k2 -> + fieldName (s1 (S (Field k1) (Field k2))) == s1FieldName where - s1FieldName = "field" + s1FieldName = "field1" -- | Test the fieldName function (should fail). testFieldNameFail :: Property testFieldNameFail = expectFailure $ property $ fieldName (s1 sampleS) == s1FieldName where - sampleS = S (Field 0) + sampleS = S (Field 0) (Field 0) s1FieldName = "Field" -- | Test showing a field of a struct. testShowField :: Property testShowField = forAll arbitrary $ \k -> - show (s1 (S (Field k))) == ("field:" ++ show k) + show (s1 (S (Field k) (Field 0))) == ("field1:" ++ show k) -- | Test showing a struct. testShowStruct :: Property -testShowStruct = forAll arbitrary $ \k -> - show (S (Field k)) == "" +testShowStruct = forAll arbitrary $ \k1 -> + forAll arbitrary $ \k2 -> + show (S (Field k1) (Field k2)) == "" + +-- | Test showing a struct. +testUpdateStruct :: Property +testUpdateStruct = + forAll arbitrary $ \k1 -> + forAll arbitrary $ \k2 -> + let f :: Field "field1" Int8 + f = Field k2 + v :: Value Int8 + v = Value Int8 f + in unField (s1 (updateField (S (Field k1) (Field 0)) v)) == k2 + + where + unField (Field x) = x + +-- | Test showing a struct. +testUpdateStructFail :: Property +testUpdateStructFail = expectFailure $ + forAll arbitrary $ \k1 -> + forAll arbitrary $ \k3 -> + let f :: Field "field" Int8 + f = Field k3 + v :: Value Int8 + v = Value Int8 f + in unField (s3 (updateField (S3 (Field k1)) v)) == k3 + + where + unField (Field x) = x -- | Test the accessorName of a field of a struct (should succeed). testAccessorNameOk :: Property testAccessorNameOk = property $ accessorName s1 == s1FieldName where - s1FieldName = "field" + s1FieldName = "field1" -- | Test the accessorName of a field of a struct (should fail). testAccessorNameFail :: Property testAccessorNameFail = expectFailure $ property $ accessorName s1 == s1FieldName where - s1FieldName = "Field" + s1FieldName = "Field1" -- | Test the typeName of a struct (should succeed). testTypeNameOk :: Property @@ -309,7 +400,7 @@ testTypeNameOk = property $ where sampleS :: S - sampleS = S (Field 0) + sampleS = S (Field 0) (Field 0) s1TypeName :: String s1TypeName = "S" @@ -322,18 +413,39 @@ testTypeNameFail = expectFailure $ property $ where sampleS :: S - sampleS = S (Field 0) + sampleS = S (Field 0) (Field 0) s1TypeName :: String s1TypeName = "s" -- | Auxiliary struct defined for testing purposes. -data S = S { s1 :: Field "field" Int8 } +data S = S { s1 :: Field "field1" Int8, s2 :: Field "field2" Word8 } instance Struct S where typeName _ = "S" - toValues s = [ Value Int8 (s1 s) ] + toValues s = [ Value Int8 (s1 s), Value Word8 (s2 s) ] + + updateField s (Value fieldTy (field :: Field fieldName a)) + | Just Refl <- sameSymbol (Proxy @"field1") (Proxy @fieldName) + , Just Refl <- testEquality Int8 fieldTy + = s { s1 = field } + | Just Refl <- sameSymbol (Proxy @"field2") (Proxy @fieldName) + , Just Refl <- testEquality Word8 fieldTy + = s { s2 = field } + | otherwise + = error $ "Unexpected field: " P.++ show field instance Typed S where - typeOf = Struct (S (Field 0)) + typeOf = Struct (S (Field 0) (Field 0)) + +-- | Auxiliary struct defined for testing purposes. +data S3 = S3 { s3 :: Field "field" Int8 } + +instance Struct S3 where + typeName _ = "S3" + + toValues s = [ Value Int8 (s3 s) ] + +instance Typed S3 where + typeOf = Struct (S3 (Field 0)) From 07ff3f72d7e19785b3c24db326d656e91297ea07 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 17 Sep 2024 05:13:18 +0000 Subject: [PATCH 116/483] copilot-core: Increase test coverage of Copilot.Core.Type.Array. Refs #555. The test coverage of copilot-core is currently reported at 75% by Hackage. Although there are exceptions to what we can test (record fields of existential types) or should test (proxies, automatically generated constructors and accessor functions), the coverage of our tests should include very top level definition and expression that can be tested without having to modify the implementation of copilot-core itself. This commit increases the test coverage for Copilot.Core.Type.Array by introducing new tests for elements in the interface not previously tested. Proxies are still reported as untested, since doing so would require modifying the code of Copilot itself to introduce strictness. --- .../tests/Test/Copilot/Core/Type/Array.hs | 130 +++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type/Array.hs b/copilot-core/tests/Test/Copilot/Core/Type/Array.hs index 208fe2fc5..e070f138b 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type/Array.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type/Array.hs @@ -10,11 +10,12 @@ import GHC.TypeNats (KnownNat, natVal) import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.QuickCheck (Gen, Property, arbitrary, - expectFailure, forAll, property, - vector, vectorOf) + chooseInt, expectFailure, forAll, + getNegative, getNonNegative, oneof, + property, vector, vectorOf) -- Internal imports: library modules being tested -import Copilot.Core.Type.Array (Array, array, arrayElems) +import Copilot.Core.Type.Array (Array, array, arrayElems, arrayUpdate) -- | All unit tests for copilot-core:Copilot.Core.Array. tests :: Test.Framework.Test @@ -30,6 +31,14 @@ tests = testArrayElemsFail , testProperty "Show for arrays" testShowArray + , testProperty "arrayElems (arrayUpdate x i v) !! i == v" + (testArrayUpdateElem (Proxy :: Proxy 5)) + , testProperty "arrayUpdate x i ((arrayElems x) !! i) == x" + (testArrayUpdateElems (Proxy :: Proxy 5)) + , testProperty "arrayUpdate fails if out of range of array" + (testArrayUpdateWrong (Proxy :: Proxy 5)) + , testProperty "array fails length is wrong" + (testArrayMakeWrongLength (Proxy :: Proxy 5)) ] -- * Individual tests @@ -60,3 +69,118 @@ testArrayElemsFail = expectFailure $ forAll (vector 3) $ \l -> testShowArray :: Property testShowArray = forAll (vector 3) $ \l -> property $ show (array l :: Array 3 Int64) == show (l :: [Int64]) + +-- | Test that updating an array updates the element appropriately (if we +-- project that element we get the value we put in). +testArrayUpdateElem :: forall n . KnownNat n => Proxy n -> Property +testArrayUpdateElem len = + forAll xsInt64 $ \ls -> + forAll position $ \p -> + forAll xInt64 $ \v -> + let -- Original array + array' :: Array n Int64 + array' = array ls + + -- Updated array + array'' :: Array n Int64 + array'' = arrayUpdate array' p v + + in arrayElems array'' !! p == v + + where + + -- Generator for lists of Int64 of known length. + xsInt64 :: Gen [Int64] + xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary + + -- Generator for element of type Int64. + xInt64 :: Gen Int64 + xInt64 = arbitrary + + -- Generator for positions within the list. + position :: Gen Int + position = chooseInt (0, fromIntegral (natVal len) - 1) + +-- | Test that updating an array updates the element appropriately (if we +-- project that element we get the value we put in). +testArrayUpdateWrong :: forall n . KnownNat n => Proxy n -> Property +testArrayUpdateWrong len = + expectFailure $ + forAll xsInt64 $ \ls -> + forAll position $ \p -> + forAll xInt64 $ \v -> + let -- Original array + array' :: Array n Int64 + array' = array ls + + -- Updated array + array'' :: Array n Int64 + array'' = arrayUpdate array' (p + 10) v + + in arrayElems array'' !! p == v + + where + + -- Generator for lists of Int64 of known length. + xsInt64 :: Gen [Int64] + xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary + + -- Generator for element of type Int64. + xInt64 :: Gen Int64 + xInt64 = arbitrary + + -- Generator for positions within the list. + position :: Gen Int + position = oneof + [ (fromIntegral (natVal len) +) . getNonNegative <$> arbitrary + , getNegative <$> arbitrary + ] + +-- | Test that making an array of a specific length fails if the list of +-- elements supplied doesn't have the same length. +testArrayMakeWrongLength :: forall n . KnownNat n => Proxy n -> Property +testArrayMakeWrongLength len = + expectFailure $ + forAll wrongLength $ \length -> + forAll (xsInt64 length) $ \ls -> + let array' :: Array n Int64 + array' = array ls + in arrayElems array' == ls + where + xsInt64 length = vectorOf length arbitrary + expectedLength = fromIntegral (natVal len) + wrongLength = (expectedLength +) . getNonNegative <$> arbitrary + +-- | Test that updating an array updates the element appropriately (if we +-- project that element we get the value we put in). +testArrayUpdateElems :: forall n . KnownNat n => Proxy n -> Property +testArrayUpdateElems len = + forAll xsInt64 $ \ls -> + forAll position $ \p -> + forAll xInt64 $ \v -> + let -- Original array + array' :: Array n Int64 + array' = array ls + + -- Updated array + e :: Int64 + e = arrayElems array' !! p + + array'' :: Array n Int64 + array'' = arrayUpdate array' p e + + in arrayElems array'' == ls + + where + + -- Generator for lists of Int64 of known length. + xsInt64 :: Gen [Int64] + xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary + + -- Generator for element of type Int64. + xInt64 :: Gen Int64 + xInt64 = arbitrary + + -- Generator for positions within the list. + position :: Gen Int + position = chooseInt (0, fromIntegral (natVal len) - 1) From 51b058059304b3048faa44e19e6dbfd204c58a42 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 30 Dec 2024 03:37:55 +0000 Subject: [PATCH 117/483] copilot-core: Document changes in CHANGELOG. Refs #555. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 3770ee808..a642472c4 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,5 +1,6 @@ -2024-11-15 +2024-12-29 * Deprecate fields of Copilot.Core.Expr.UExpr. (#565) + * Increase test coverage. (#555) 2024-11-07 * Version bump (4.1). (#561) From 5ac80921bc95846804006a64699070d7145d0e1a Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 08:39:48 +0100 Subject: [PATCH 118/483] copilot-c99: Add specific `Trigger` representation. Refs #296. The current implementation of Copilot does not allow using the same trigger handler name multiple times, since the C99 backend produces multiple repeated declarations, which is invalid. Consequently, the code generator assumes that trigger names are unique. To allow for multiple triggers that trigger the same handler, we need to introduce a separate construct in triggers that acts as a unique identifier. This commit introduces an internal datatype to represent a `Trigger` with a unique name that is different from that of the function called when triggered. --- copilot-c99/copilot-c99.cabal | 1 + .../src/Copilot/Compile/C99/Representation.hs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 copilot-c99/src/Copilot/Compile/C99/Representation.hs diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index f6a196b81..4e441d314 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -59,6 +59,7 @@ library , Copilot.Compile.C99.External , Copilot.Compile.C99.Compile , Copilot.Compile.C99.Settings + , Copilot.Compile.C99.Representation test-suite unit-tests type: diff --git a/copilot-c99/src/Copilot/Compile/C99/Representation.hs b/copilot-c99/src/Copilot/Compile/C99/Representation.hs new file mode 100644 index 000000000..886808a8a --- /dev/null +++ b/copilot-c99/src/Copilot/Compile/C99/Representation.hs @@ -0,0 +1,21 @@ +-- | C99 backend specific versions of selected `Copilot.Core` datatypes. +module Copilot.Compile.C99.Representation + ( UniqueTrigger (..) + , UniqueTriggerId + , mkUniqueTriggers + ) + where + +import Copilot.Core ( Trigger (..) ) + +-- | Internal unique name for a trigger. +type UniqueTriggerId = String + +-- | A `Copilot.Core.Trigger` with an unique name. +data UniqueTrigger = UniqueTrigger UniqueTriggerId Trigger + +-- | Given a list of triggers, make their names unique. +mkUniqueTriggers :: [Trigger] -> [UniqueTrigger] +mkUniqueTriggers ts = zipWith mkUnique ts [0..] + where + mkUnique t@(Trigger name _ _) n = UniqueTrigger (name ++ "_" ++ show n) t From e6356ef260524abbaf5891e2822bcb0ef5d3d3cf Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 08:39:48 +0100 Subject: [PATCH 119/483] copilot-c99: Adapt `mkStep` to use unique triggers. Refs #296. The current implementation of Copilot does not allow using the same trigger handler name multiple times, since the C99 backend produces multiple repeated declarations, which is invalid. A prior commit has introduced a representation of triggers with unique IDs, allowing us to distinguish between a monitor for one condition within a Copilot spec vs the external C function being called when the condition becomes true. This commit updates the generation of the `step` function to use unique names for the triggers. --- .../src/Copilot/Compile/C99/CodeGen.hs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs index e71e7a0df..ce822e558 100644 --- a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs +++ b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs @@ -36,14 +36,17 @@ import Copilot.Core ( Expr (..), Id, Stream (..), Struct (..), Trigger (..), Type (..), UExpr (..), Value (..), fieldName, typeSize ) -- Internal imports -import Copilot.Compile.C99.Error ( impossible ) -import Copilot.Compile.C99.Expr ( constArray, transExpr ) -import Copilot.Compile.C99.External ( External (..) ) -import Copilot.Compile.C99.Name ( argNames, argTempNames, generatorName, - guardName, indexName, streamAccessorName, - streamName ) -import Copilot.Compile.C99.Settings ( CSettings, cSettingsStepFunctionName ) -import Copilot.Compile.C99.Type ( transType ) +import Copilot.Compile.C99.Error ( impossible ) +import Copilot.Compile.C99.Expr ( constArray, transExpr ) +import Copilot.Compile.C99.External ( External (..) ) +import Copilot.Compile.C99.Name ( argNames, argTempNames, + generatorName, guardName, + indexName, streamAccessorName, + streamName ) +import Copilot.Compile.C99.Settings ( CSettings, + cSettingsStepFunctionName ) +import Copilot.Compile.C99.Type ( transType ) +import Copilot.Compile.C99.Representation ( UniqueTrigger (..) ) -- * Externs @@ -162,7 +165,7 @@ mkGenFunArray _name _nameArg _expr _ty = -- * Monitor processing -- | Define the step function that updates all streams. -mkStep :: CSettings -> [Stream] -> [Trigger] -> [External] -> C.FunDef +mkStep :: CSettings -> [Stream] -> [UniqueTrigger] -> [External] -> C.FunDef mkStep cSettings streams triggers exts = C.FunDef Nothing void (cSettingsStepFunctionName cSettings) [] declns stmts where @@ -271,8 +274,8 @@ mkStep cSettings streams triggers exts = -- 2. Assigning a struct to a temporary variable defensively ensures that -- any modifications that the handler called makes to the struct argument -- will not affect the internals of the monitoring code. - mkTriggerCheck :: Trigger -> ([C.Decln], C.Stmt) - mkTriggerCheck (Trigger name _guard args) = + mkTriggerCheck :: UniqueTrigger -> ([C.Decln], C.Stmt) + mkTriggerCheck (UniqueTrigger uniqueName (Trigger name _guard args)) = (aTmpDeclns, triggerCheckStmt) where aTmpDeclns :: [C.Decln] @@ -285,12 +288,14 @@ mkStep cSettings streams triggers exts = triggerCheckStmt :: C.Stmt triggerCheckStmt = C.If guard' fireTrigger where - guard' = C.Funcall (C.Ident $ guardName name) [] + guard' = C.Funcall (C.Ident $ guardName uniqueName) [] -- The body of the if-statement. This consists of statements that -- assign the values of the temporary variables, following by a -- final statement that passes the temporary variables to the -- handler function. + -- Note that we call 'name' here instead of 'uniqueName', as 'name' + -- is the name of the actual external function. fireTrigger = map C.Expr argAssigns ++ [C.Expr $ C.Funcall (C.Ident name) @@ -305,7 +310,7 @@ mkStep cSettings streams triggers exts = updateVar aTempName aArgName ty aArgNames :: [C.Ident] - aArgNames = take (length args) (argNames name) + aArgNames = take (length args) (argNames uniqueName) -- Build an expression to pass a temporary variable as argument -- to a trigger handler. @@ -323,7 +328,7 @@ mkStep cSettings streams triggers exts = _ -> C.Ident aTempName aTempNames :: [String] - aTempNames = take (length args) (argTempNames name) + aTempNames = take (length args) (argTempNames uniqueName) -- * Auxiliary functions From c1f273817e166e134b2e937579d63f327bb72c4d Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 08:39:48 +0100 Subject: [PATCH 120/483] copilot-c99: Adapt top-level compilation functions to use unique triggers. Refs #296. The current implementation of Copilot does not allow using the same trigger handler name multiple times, since the C99 backend produces multiple repeated declarations, which is invalid. Prior commits have introduced a representation of triggers with unique IDs (allowing us to distinguish between a monitor for one condition within a Copilot spec vs the external C function being called when the condition becomes true), and have adapted low-level functions accordingly. This commit adapts the top-level compilation functions in the same way, by generating trigger guards and argument functions using the unique names. --- .../src/Copilot/Compile/C99/Compile.hs | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Compile.hs b/copilot-c99/src/Copilot/Compile/C99/Compile.hs index 515078258..b7e676a3d 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Compile.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Compile.hs @@ -23,17 +23,21 @@ import Copilot.Core ( Expr (..), Spec (..), Stream (..), Struct (..), Value (..) ) -- Internal imports -import Copilot.Compile.C99.CodeGen ( mkAccessDecln, mkBuffDecln, mkExtCpyDecln, - mkExtDecln, mkGenFun, mkGenFunArray, - mkIndexDecln, mkStep, mkStructDecln, - mkStructForwDecln ) -import Copilot.Compile.C99.External ( External, gatherExts ) -import Copilot.Compile.C99.Name ( argNames, generatorName, - generatorOutputArgName, guardName ) -import Copilot.Compile.C99.Settings ( CSettings, cSettingsOutputDirectory, - cSettingsStepFunctionName, - mkDefaultCSettings ) -import Copilot.Compile.C99.Type ( transType ) +import Copilot.Compile.C99.CodeGen ( mkAccessDecln, mkBuffDecln, + mkExtCpyDecln, mkExtDecln, + mkGenFun, mkGenFunArray, + mkIndexDecln, mkStep, + mkStructDecln, mkStructForwDecln ) +import Copilot.Compile.C99.External ( External, gatherExts ) +import Copilot.Compile.C99.Name ( argNames, generatorName, + generatorOutputArgName, guardName ) +import Copilot.Compile.C99.Settings ( CSettings, + cSettingsOutputDirectory, + cSettingsStepFunctionName, + mkDefaultCSettings ) +import Copilot.Compile.C99.Type ( transType ) +import Copilot.Compile.C99.Representation ( UniqueTrigger (..), + mkUniqueTriggers ) -- | Compile a specification to a .h and a .c file. -- @@ -90,12 +94,13 @@ compileC cSettings spec = C.TransUnit declns funs declns = mkExts exts ++ mkGlobals streams - funs = mkGenFuns streams triggers - ++ [mkStep cSettings streams triggers exts] + funs = mkGenFuns streams uniqueTriggers + ++ [mkStep cSettings streams uniqueTriggers exts] - streams = specStreams spec - triggers = specTriggers spec - exts = gatherExts streams triggers + streams = specStreams spec + triggers = specTriggers spec + uniqueTriggers = mkUniqueTriggers triggers + exts = gatherExts streams triggers -- Make declarations for copies of external variables. mkExts :: [External] -> [C.Decln] @@ -110,7 +115,7 @@ compileC cSettings spec = C.TransUnit declns funs indexDecln (Stream sId _ _ _ ) = mkIndexDecln sId -- Make generator functions, including trigger arguments. - mkGenFuns :: [Stream] -> [Trigger] -> [C.FunDef] + mkGenFuns :: [Stream] -> [UniqueTrigger] -> [C.FunDef] mkGenFuns streamList triggerList = map accessDecln streamList ++ map streamGen streamList ++ concatMap triggerGen triggerList @@ -122,11 +127,11 @@ compileC cSettings spec = C.TransUnit declns funs streamGen (Stream sId _ expr ty) = exprGen (generatorName sId) (generatorOutputArgName sId) expr ty - triggerGen :: Trigger -> [C.FunDef] - triggerGen (Trigger name guard args) = guardDef : argDefs + triggerGen :: UniqueTrigger -> [C.FunDef] + triggerGen (UniqueTrigger uniqueName (Trigger _name guard args)) = guardDef : argDefs where - guardDef = mkGenFun (guardName name) guard Bool - argDefs = zipWith argGen (argNames name) args + guardDef = mkGenFun (guardName uniqueName) guard Bool + argDefs = zipWith argGen (argNames uniqueName) args argGen :: String -> UExpr -> C.FunDef argGen argName (UExpr ty expr) = From 3f866c8bfea320ff43afda403b8e470af0caf9de Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 08:39:48 +0100 Subject: [PATCH 121/483] copilot-c99: Remove duplicate trigger declarations in .h output. Refs #296. The current implementation of Copilot's C99 backend produces multiple repeated declarations when the same trigger is used multiple times, which is invalid C code. This commit modifies the code that generates the header file to prevent the same trigger from being declared multiple times. --- .../src/Copilot/Compile/C99/Compile.hs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Compile.hs b/copilot-c99/src/Copilot/Compile/C99/Compile.hs index b7e676a3d..7ee083968 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Compile.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Compile.hs @@ -6,8 +6,9 @@ module Copilot.Compile.C99.Compile ) where -- External imports -import Data.List ( nub, union ) +import Data.List ( nub, nubBy, union ) import Data.Maybe ( mapMaybe ) +import Data.Type.Equality ( testEquality, (:~:)(Refl) ) import Data.Typeable ( Typeable ) import Language.C99.Pretty ( pretty ) import qualified Language.C99.Simple as C @@ -160,7 +161,9 @@ compileH cSettings spec = C.TransUnit declns [] exprs = gatherExprs streams triggers exts = gatherExts streams triggers streams = specStreams spec - triggers = specTriggers spec + + -- Remove duplicates due to multiple guards for the same trigger. + triggers = nubBy compareTrigger (specTriggers spec) mkStructForwDeclns :: [UExpr] -> [C.Decln] mkStructForwDeclns es = mapMaybe mkDecln uTypes @@ -261,3 +264,21 @@ gatherExprs streams triggers = map streamUExpr streams where streamUExpr (Stream _ _ expr ty) = UExpr ty expr triggerUExpr (Trigger _ guard args) = UExpr Bool guard : args + +-- | We consider triggers to be equal, if their names match and the number and +-- types of arguments. +compareTrigger :: Trigger -> Trigger -> Bool +compareTrigger (Trigger name1 _ args1) (Trigger name2 _ args2) + = name1 == name2 && compareArguments args1 args2 + + where + compareArguments :: [UExpr] -> [UExpr] -> Bool + compareArguments [] [] = True + compareArguments [] _ = False + compareArguments _ [] = False + compareArguments (x:xs) (y:ys) = compareUExpr x y && compareArguments xs ys + + compareUExpr :: UExpr -> UExpr -> Bool + compareUExpr (UExpr ty1 _) (UExpr ty2 _) + | Just Refl <- testEquality ty1 ty2 = True + | otherwise = False From 4cd80053f7a5bec550b11c379c156fb8866fa413 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 08:39:48 +0100 Subject: [PATCH 122/483] copilot-c99: Disallow conflicting trigger definitions. Refs #296. The current implementation of Copilot does not allow using the same trigger handler name multiple times, since the C99 backend produces multiple repeated declarations, which is invalid. Prior commits have modified the C99 backend to now allow for multiple triggers with the same handler. However, the resulting code is only valid in C if the triggers always have the same type signatures, since C does not support polymorphism. This commit adds a check to the compilation step to ensure that triggers used multiple times are always used with the same types (and, consequently, the same arity). --- .../src/Copilot/Compile/C99/Compile.hs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Compile.hs b/copilot-c99/src/Copilot/Compile/C99/Compile.hs index 7ee083968..1467af403 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Compile.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Compile.hs @@ -47,12 +47,20 @@ import Copilot.Compile.C99.Representation ( UniqueTrigger (..), -- The second argument is used as prefix for the .h and .c files generated. compileWith :: CSettings -> String -> Spec -> IO () compileWith cSettings prefix spec - | null (specTriggers spec) + | null triggers = do hPutStrLn stderr $ "Copilot error: attempt at compiling empty specification.\n" ++ "You must define at least one trigger to generate C monitors." exitFailure + | incompatibleTriggers triggers + = do hPutStrLn stderr $ + "Copilot error: attempt at compiling specification with conflicting " + ++ "trigger definitions.\n" + ++ "Multiple triggers have the same name, but different argument " + ++ "types.\n" + exitFailure + | otherwise = do let cFile = render $ pretty $ C.translate $ compileC cSettings spec hFile = render $ pretty $ C.translate $ compileH cSettings spec @@ -74,6 +82,24 @@ compileWith cSettings prefix spec writeFile (dir prefix ++ ".c") $ cMacros ++ cFile writeFile (dir prefix ++ ".h") hFile writeFile (dir prefix ++ "_types.h") typeDeclnsFile + where + triggers = specTriggers spec + + -- Check that two triggers do no conflict, that is: if their names are + -- equal, the types of their arguments should be equal as well. + incompatibleTriggers :: [Trigger] -> Bool + incompatibleTriggers = pairwiseAny conflict + where + conflict :: Trigger -> Trigger -> Bool + conflict t1@(Trigger name1 _ _) t2@(Trigger name2 _ _) = + name1 == name2 && not (compareTrigger t1 t2) + + -- True if the function holds for any pair of elements. We assume that + -- the function is commutative. + pairwiseAny :: (a -> a -> Bool) -> [a] -> Bool + pairwiseAny _ [] = False + pairwiseAny _ (_:[]) = False + pairwiseAny f (x:xs) = any (f x) xs || pairwiseAny f xs -- | Compile a specification to a .h and a .c file. -- From 1d389490d6e1017932679bb7a639dd9856bd04f8 Mon Sep 17 00:00:00 2001 From: Frank Dedden Date: Fri, 20 Dec 2024 09:02:26 +0100 Subject: [PATCH 123/483] copilot-c99: Document changes in CHANGELOG. Refs #296. --- copilot-c99/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 50c704f20..635e0021a 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,5 +1,6 @@ -2024-11-14 +2024-12-20 * Remove uses of Copilot.Core.Expr.UExpr.uExprType. (#565) + * Allow using same trigger name in multiple declarations. (#296) 2024-11-07 * Version bump (4.1). (#561) From 5aee0c3e485177070959f9c360cba8beb676be31 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:20:32 -0800 Subject: [PATCH 124/483] copilot: Bump upper version constraint on filepath. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on filepath so that Copilot can be installed with version 1.5.2.0. --- copilot/copilot.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index c8383ae9a..f645bf43d 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -50,7 +50,7 @@ library base >= 4.9 && < 5 , optparse-applicative >= 0.14 && < 0.19 , directory >= 1.3 && < 1.4 - , filepath >= 1.4 && < 1.5 + , filepath >= 1.4 && < 1.6 , copilot-core >= 4.1 && < 4.2 , copilot-theorem >= 4.1 && < 4.2 From 818ca231a623bbf97c54fed18db17300e5f4cc35 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:26:18 -0800 Subject: [PATCH 125/483] copilot-c99: Bump upper version constraint on filepath. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on filepath so that Copilot can be installed with version 1.5.2.0. --- copilot-c99/copilot-c99.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 4e441d314..09e987d81 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -41,7 +41,7 @@ library ghc-options : -Wall build-depends : base >= 4.9 && < 5 , directory >= 1.3 && < 1.4 - , filepath >= 1.4 && < 1.5 + , filepath >= 1.4 && < 1.6 , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 From ddef3484d44a3ff080d1d0370fc95a6b683b5eab Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:27:55 -0800 Subject: [PATCH 126/483] copilot-language: Bump upper version constraint on containers. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on containers so that Copilot can be installed with version 0.7. --- copilot-language/copilot-language.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index ad0ec0725..74d501421 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -38,7 +38,7 @@ library build-depends: base >= 4.9 && < 5 , array >= 0.5 && < 0.6 - , containers >= 0.4 && < 0.7 + , containers >= 0.4 && < 0.8 , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 From 9100e977d1853b0e8ce930835b133ce53484a849 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:30:19 -0800 Subject: [PATCH 127/483] copilot-libraries: Bump upper version constraint on containers. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on containers so that Copilot can be installed with version 0.7. --- copilot-libraries/copilot-libraries.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index fc312c91d..709d18236 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -38,7 +38,7 @@ library build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.7 + , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 , copilot-language >= 4.1 && < 4.2 From 0c7c2667efec5cc5402f762d4b15a86ab7de1a1e Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:31:06 -0800 Subject: [PATCH 128/483] copilot-theorem: Bump upper version constraint on containers. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on containers so that Copilot can be installed with version 0.7. --- copilot-theorem/copilot-theorem.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 9d7dd0fda..f31c49805 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -48,7 +48,7 @@ library build-depends : base >= 4.9 && < 5 , bimap (>= 0.3 && < 0.4) || (>= 0.5 && < 0.6) , bv-sized >= 1.0.2 && < 1.1 - , containers >= 0.4 && < 0.7 + , containers >= 0.4 && < 0.8 , data-default >= 0.7 && < 0.8 , directory >= 1.3 && < 1.4 , libBF >= 0.6.2 && < 0.7 From 40d5f16538e3252d4d5c1ad5a626ef09e6540960 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:32:49 -0800 Subject: [PATCH 129/483] copilot-theorem: Bump upper version constraint on data-default. Refs #570. In order to keep Copilot effectively working in the current Haskell ecosystem, and with new versions of GHC, we want to extend the versions of dependencies that Copilot can be installed with as much as possible to include newly released versions. This commit relaxes the upper bounds on the version constraints on data-default so that Copilot can be installed with version 0.8.0.0. --- copilot-theorem/copilot-theorem.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index f31c49805..135d75d5a 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -49,7 +49,7 @@ library , bimap (>= 0.3 && < 0.4) || (>= 0.5 && < 0.6) , bv-sized >= 1.0.2 && < 1.1 , containers >= 0.4 && < 0.8 - , data-default >= 0.7 && < 0.8 + , data-default >= 0.7 && < 0.9 , directory >= 1.3 && < 1.4 , libBF >= 0.6.2 && < 0.7 , mtl >= 2.0 && < 2.4 From 6d9a674b3eaf01d3e2ca5e7b59816d03043cc085 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:38:40 -0800 Subject: [PATCH 130/483] copilot: Document changes in CHANGELOG. Refs #570. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index ce4a1c37a..40e0bdc24 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-02 + * Bump upper version constraint on filepath. (#570) + 2024-11-07 * Version bump (4.1). (#561) * Update contribution guidelines. (#476) From 66b9f84b393fabbc6445b65812f38c6420d09f5b Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:40:18 -0800 Subject: [PATCH 131/483] copilot-c99: Document changes in CHANGELOG. Refs #570. --- copilot-c99/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 635e0021a..dcd314cad 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,6 +1,7 @@ -2024-12-20 +2025-01-02 * Remove uses of Copilot.Core.Expr.UExpr.uExprType. (#565) * Allow using same trigger name in multiple declarations. (#296) + * Bump upper version constraint on filepath. (#570) 2024-11-07 * Version bump (4.1). (#561) From 68cd36187c34b64d37ec955d31d1d7740d09881c Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:41:39 -0800 Subject: [PATCH 132/483] copilot-language: Document changes in CHANGELOG. Refs #570. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 1d205da0e..64fe1728c 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-02 + * Bump upper version constraint on containers. (#570) + 2024-11-07 * Version bump (4.1). (#561) * Reject duplicate externs in properties and theorems. (#536) From c015b67cb416cc14f1d7ba175c8720855be228a1 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:42:58 -0800 Subject: [PATCH 133/483] copilot-libraries: Document changes in CHANGELOG. Refs #570. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index c4ce6ddcf..4a7ed2674 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-02 + * Bump upper version constraint on containers. (#570) + 2024-11-07 * Version bump (4.1). (#561) * Standardize changelog format. (#550) From a31eb047e5d7e9d1f1d79d42898a667d05fd2389 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Thu, 2 Jan 2025 13:47:12 -0800 Subject: [PATCH 134/483] copilot-theorem: Document changes in CHANGELOG. Refs #570. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 7508a978d..e17fe667f 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ -2024-11-15 +2025-01-02 * Remove uses of Copilot.Core.Expr.UExpr.uExprType,uExprExpr. (#565) + * Bump upper constraint on containers, data-default. (#570) 2024-11-07 * Version bump (4.1). (#561) From b447d1c00e3aa1c3c80954aea1f4483800bd5457 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 12 Nov 2024 11:31:57 -0500 Subject: [PATCH 135/483] copilot-core: Define generic implementations of Struct and Typed methods. Refs #564. Currently, using structs in Copilot requires defining instances of several type classes. Defining those instances is unnecessarily cumbersome, and it exposes the user to a substantial amount of Haskell even when they want to stay at the level of the Copilot language. This commit adds functionality for implementing the class methods of `Struct` and `Typed` using `GHC.Generics`. As such, one can now easily define instances of these classes by deriving a `Generic` instance for the struct data type, i.e., ```hs data MyStructType = ... deriving Generic instance Struct MyStructType where typeName = typeNameDefault toValues = toValuesDefault updateField = updateFieldDefault instance Typed MyStructType where typeOf = typeOfDefault ``` This work is based off of an initial implementation by Marten Wijnja (@Qqwy). Note that I do not yet change any of the default implementations of any `Struct` or `Typed` methods. This is because several `Struct` instances in the wild currently do not define implementations of `updateField`, and moreover, they also do not define `Generic` instances for the corresponding data types. As such, changing the default implementation of `updateField` to use a `Generic`-based default would cause these instances in the wild to no longer compile. We will explore changing the default implementations after a suitable transition period. --- copilot-core/src/Copilot/Core/Type.hs | 173 +++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index b31a35094..8ab5f195a 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -4,8 +4,9 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} -- | @@ -20,6 +21,7 @@ module Copilot.Core.Type ( Type (..) , Typed (..) + , typeOfDefault , UType (..) , SimpleType (..) @@ -28,25 +30,32 @@ module Copilot.Core.Type , Value (..) , toValues + , toValuesDefault , Field (..) , typeName + , typeNameDefault , Struct , fieldName , accessorName , updateField + , updateFieldDefault ) where -- External imports +import Data.Char (isLower, isUpper, toLower) +import Data.Coerce (coerce) import Data.Int (Int16, Int32, Int64, Int8) import Data.List (intercalate) import Data.Proxy (Proxy (..)) import Data.Type.Equality as DE import Data.Typeable (Typeable, eqT, typeRep) import Data.Word (Word16, Word32, Word64, Word8) +import GHC.Generics (Datatype (..), D1, Generic (..), K1 (..), M1 (..), + U1 (..), (:*:) (..)) import GHC.TypeLits (KnownNat, KnownSymbol, Symbol, natVal, sameNat, - symbolVal) + sameSymbol, symbolVal) -- Internal imports import Copilot.Core.Type.Array (Array) @@ -272,3 +281,163 @@ data UType = forall a . Typeable a => UType { uTypeType :: Type a } instance Eq UType where UType ty1 == UType ty2 = typeRep ty1 == typeRep ty2 + +-- * GHC.Generics-based defaults + +-- | A default implementation of 'typeName' that leverages 'Generic'. In order +-- to use this, make sure you derive a 'Generic' instance for your data type and +-- then define @'typeName' = 'typeNameDefault'@ in its 'Struct' instance. +-- +-- This generates a struct name that consists of the name of the original +-- Haskell data type, but converted to snake_case. +typeNameDefault :: (Generic a, GDatatype (Rep a)) => a -> String +typeNameDefault = convert . gTypeName . from + where + convert :: String -> String + convert = convert' True True + + convert' :: Bool -- ^ Is this the first letter + -> Bool -- ^ Was the previous letter capital + -> String -- ^ Remainder of the string + -> String + convert' _ _ [] = [] + convert' _ v [x] + | v && isUpper x = toLower x : [] + | isUpper x = '_' : toLower x : [] + | otherwise = x : [] + convert' b v (x1:x2:xs) + | not b && isUpper x1 && (isLower x2 || not v) + = '_' : toLower x1 : convert' False (isUpper x1) (x2:xs) + | otherwise + = toLower x1 : convert' False (isUpper x1) (x2:xs) + +-- | A default implementation of 'toValues' that leverages 'Generic'. In order +-- to use this, make sure you derive a 'Generic' instance for your data type and +-- then define @'toValues' = 'toValuesDefault'@ in its 'Struct' instance. +toValuesDefault :: (Generic a, GStruct (Rep a)) => a -> [Value a] +toValuesDefault x = coerce $ gToValues $ from x + +-- | A default implementation of 'updateField' that leverages 'Generic'. In +-- order to use this, make sure you derive a 'Generic' instance for your data +-- type and then define @'updateField' = 'updateFieldDefault'@ in its 'Struct' +-- instance. +updateFieldDefault :: (Generic a, GStruct (Rep a)) => a -> Value t -> a +updateFieldDefault a v@(Value _ field) + | updated = to a' + | otherwise = error $ "Unexpected field: " ++ show field + where + (a', updated) = gUpdateField (from a) v + +-- | A default implementation of 'typeOf' that leverages 'Generic'. In order to +-- use this, make sure you derive a 'Generic' instance for your data type and +-- then define @'typeOf' = 'typeOfDefault'@ in its 'Typed' instance. +typeOfDefault :: + forall a. (Typed a, Struct a, Generic a, GTypedStruct (Rep a)) => Type a +typeOfDefault = Struct $ to $ gStructPlaceholder @(Rep a) @() + +-- ** Generic-based classes (not exported) + +-- | Capture the name of a Haskell data type from its 'Generic' metadata. +class GDatatype f where + -- | Returns the name of a Haskell data type. (Note that this differs from + -- 'typeName', which is expected to return the name of the struct in the + -- /target/ language). + gTypeName :: f p -> String + +-- | The only 'GDatatype' instance we need is for 'D1', which describes +-- 'Datatype' metadata (@d@). We ignore all other metadata (@_f@). +instance Datatype d => GDatatype (D1 d _f) where + gTypeName = datatypeName + +-- | Perform struct-related operations on 'Generic' representation types. +class GStruct f where + -- | Transforms all the struct representation's fields into a list of values. + gToValues :: f p -> [Value (f p)] + + -- | Update the value of a struct representation's field. This returns two + -- things: + -- + -- 1. @f p@: The struct representation, but with the field updated. + -- + -- 2. 'Bool': This is 'True' if the field was successfully updated and 'False' + -- otherwise. If this returns 'False', it is the responsibility of the + -- caller to raise an error. + gUpdateField :: f p -> Value t -> (f p, Bool) + +-- | 'U1' represents a data constructor with no fields. As such, 'gToValues' +-- returns an empty list of 'Value's, and 'gUpdateField' does not update +-- anything. +instance GStruct U1 where + gToValues U1 = [] + gUpdateField u _ = (u, False) + +-- | 'M1' is only used to store metadata, which the 'GStruct' class does not +-- make use of. As such, this instance discards the metadata and recurses. +instance GStruct f => GStruct (M1 _i _c f) where + gToValues (M1 x) = coerce (gToValues x) + gUpdateField (M1 x) v = (M1 x', updated) + where + (x', updated) = gUpdateField x v + +-- | @(':*:')@ represents a data constructor with multiple fields. +instance (GStruct f, GStruct g) => GStruct (f :*: g) where + -- Recursively compute two lists of Values and append them. + gToValues (f :*: g) = coerce (gToValues f) ++ coerce (gToValues g) + -- Recursively attempt to update the field in both branches and combine the + -- updated branches. We will have successfully updated the field if either + -- branch was successfully updated. + gUpdateField (f :*: g) v = (f' :*: g', updatedF || updatedG) + where + (f', updatedF) = gUpdateField f v + (g', updatedG) = gUpdateField g v + +-- | 'K1' represents a single field in a data constructor. This is the base +-- case. +instance (KnownSymbol name, Typed ty, c ~ Field name ty) => + GStruct (K1 _i c) where + -- Now that we have the field, return it in a singleton list. + gToValues (K1 field) = [Value typeOf field] + -- In order to update the field, we check that the field names and types + -- match. If they do, return the updated field and declare the update as + -- successful. Otherwise, return the old field and declare the update as + -- unsuccessful. + gUpdateField (K1 oldField) (Value newTy (newField :: Field newName newTy)) = + case (sameSymbol pName pNewName, testEquality ty newTy) of + (Just Refl, Just Refl) -> (K1 newField, True) + _ -> (K1 oldField, False) + where + pName = Proxy @name + pNewName = Proxy @newName + ty = typeOf @ty + +-- | Compute a 'Generic' placeholder value to use for a struct type. +class GTypedStruct f where + -- | A placeholder value to supply to use in a generic implementation of + -- 'typeOf' for a struct type. + gStructPlaceholder :: f p + +-- | 'U1' represents a data constructor with no fields. As such, +-- 'gStructPlaceholder' simply returns the data constructor with no other +-- information. +instance GTypedStruct U1 where + gStructPlaceholder = U1 + +-- | 'M1' is only used to store metadata, which the 'GTypedStruct' class does +-- not make use of. As such, this instance recursively computes a placeholder +-- value without inspecting the metadata. +instance GTypedStruct f => GTypedStruct (M1 _i _c f) where + gStructPlaceholder = M1 gStructPlaceholder + +-- | @(':*:')@ represents a data constructor with multiple fields. As such, +-- 'gStructPlaceholder' recursively computes placeholders for each field and +-- combines them into the overall data constructor. +instance (GTypedStruct f, GTypedStruct g) => GTypedStruct (f :*: g) where + gStructPlaceholder = gStructPlaceholder :*: gStructPlaceholder + +-- | 'K1' represents a single field in a data constructor. This is the base +-- case. This instance computes a placeholder value that works for any field of +-- any type. +instance (c ~ Field name ty) => GTypedStruct (K1 _i c) where + -- We use 'undefined' as the actual value for the 'Field' because Copilot + -- never inspects the value. + gStructPlaceholder = K1 $ Field undefined From 428209ea265f2aecbca838273888365744f7667f Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 12 Nov 2024 12:37:55 -0500 Subject: [PATCH 136/483] copilot-core: Warn about future generic default for updateField. Refs #564. Currently, using structs in Copilot requires defining instances of several type classes. Defining those instances is unnecessarily cumbersome, and it exposes the user to a substantial amount of Haskell even when they want to stay at the level of the Copilot language. In the previous commit, we added `Generic`-based defaults for several type class methods, including the `updateField` method of `Struct`. In the future, we plan to change the default implementation for `updateField` such that it will require a `Generic` instance. This will break any existing `Struct` instance that omits an implementation of `updateField` and also does not define a `Generic` instance for the struct data type. Unfortunately, there does not appear to be a way for GHC to warn about this combination of properties. This commit adds a verbal warning to the `updateField` Haddocks to make users aware of this potential problem. --- copilot-core/src/Copilot/Core/Type.hs | 37 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 8ab5f195a..814a22c75 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -70,24 +70,35 @@ class Struct a where toValues :: a -> [Value a] -- | Update the value of a struct field. This is only used by the Copilot - -- interpreter, so if you do not plan to use the interpreter, it is safe to - -- omit an implementation of this method. + -- interpreter. -- - -- In order to implement 'updateField', you should do the following for each - -- 'Field' in a struct: + -- If you do not plan to use the interpreter, you can omit an implementation + -- of this method. If you do so, it is recommended that you derive a 'Generic' + -- instance for the struct data type. This is because in a future release, the + -- default implementation of 'updateField' (which will be picked if there is + -- not a manually written implementation) will be changed to require a + -- 'Generic' instance. -- - -- 1. Check that the name of the 'Field' matches the name of the supplied - -- 'Value' (using 'GHC.TypeLits.sameSymbol'). + -- In order to implement 'updateField', pick one of the following approaches: -- - -- 2. Check that the type of the 'Field' matches the 'Type' of the supplied - -- 'Value' (using 'DE.testEquality'). + -- * Derive a 'Generic' instance for the struct data type and then define + -- @'updateField' = 'updateFieldDefault'@ in the 'Struct' instance. -- - -- 3. If both (1) and (2) succeed, update the corresponding struct field using - -- a record update. + -- * Manually implement 'updateField' by doing the following for each 'Field' + -- in a struct: -- - -- For a complete end-to-end example that demonstrates how to implement - -- 'updateField' and use it in the Copilot interpreter, see the - -- @examples/StructsUpdateField.hs@ example in the @copilot@ library. + -- 1. Check that the name of the 'Field' matches the name of the supplied + -- 'Value' (using 'GHC.TypeLits.sameSymbol'). + -- + -- 2. Check that the type of the 'Field' matches the 'Type' of the supplied + -- 'Value' (using 'DE.testEquality'). + -- + -- 3. If both (1) and (2) succeed, update the corresponding struct field + -- using a record update. + -- + -- For a complete end-to-end example that demonstrates how to manually + -- implement 'updateField' and use it in the Copilot interpreter, see the + -- @examples/StructsUpdateField.hs@ example in the @copilot@ library. updateField :: a -> Value t -> a updateField = error $ unlines [ "Field updates not supported for this type." From ae41a73b4892359c5e192db4f92d7be19c44af3f Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 12 Nov 2024 11:48:50 -0500 Subject: [PATCH 137/483] copilot: Update struct examples to use generic method implementations. Refs #564. Currently, using structs in Copilot requires defining instances of several type classes. Defining those instances is unnecessarily cumbersome, and it exposes the user to a substantial amount of Haskell even when they want to stay at the level of the Copilot language. In a previous commit, we added `Generic`-based defaults for the methods of the `Struct` and `Typed` classes. In this commit, we leverage these default implementations to remove much of the boilerplate code needed to define `Struct` and `Typed` instances in the struct-related `copilot` examples. Note that I have intentionally _not_ used `updateFieldDefault` in the `StructsUpdateField.hs` example, as that example is intended to demonstrate how one would implement `updateField` by hand. This work is based off of an initial implementation by Marten Wijnja (@Qqwy). --- copilot/examples/Structs.hs | 41 +++++++++++------------ copilot/examples/StructsUpdateField.hs | 46 ++++++++++++++------------ copilot/examples/what4/Structs.hs | 41 +++++++++++------------ 3 files changed, 62 insertions(+), 66 deletions(-) diff --git a/copilot/examples/Structs.hs b/copilot/examples/Structs.hs index 559a5be29..1b036fe5e 100644 --- a/copilot/examples/Structs.hs +++ b/copilot/examples/Structs.hs @@ -1,28 +1,29 @@ -- | An example showing how specifications involving structs (in particular, -- nested structs) are compiled to C using copilot-c99. -{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} module Main where import qualified Prelude as P import Control.Monad (void, forM_) +import GHC.Generics (Generic) import Language.Copilot import Copilot.Compile.C99 -- | Definition for `Volts`. data Volts = Volts - { numVolts :: Field "numVolts" Word16 - , flag :: Field "flag" Bool - } + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + deriving Generic -- | `Struct` instance for `Volts`. instance Struct Volts where - typeName _ = "volts" - toValues volts = [ Value Word16 (numVolts volts) - , Value Bool (flag volts) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- Note that we do not implement `updateField` here. `updateField` is only -- needed to make updates to structs work in the Copilot interpreter, and we -- do not use the interpreter in this example. (See @@ -31,29 +32,25 @@ instance Struct Volts where -- | `Volts` instance for `Typed`. instance Typed Volts where - typeOf = Struct (Volts (Field 0) (Field False)) + typeOf = typeOfDefault data Battery = Battery - { temp :: Field "temp" Word16 - , volts :: Field "volts" (Array 10 Volts) - , other :: Field "other" (Array 10 (Array 5 Word32)) - } + { temp :: Field "temp" Word16 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + deriving Generic -- | `Battery` instance for `Struct`. instance Struct Battery where - typeName _ = "battery" - toValues battery = [ Value typeOf (temp battery) - , Value typeOf (volts battery) - , Value typeOf (other battery) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- Note that we do not implement `updateField` here for the same reasons as in -- the `Struct Volts` instance above. --- | `Battery` instance for `Typed`. Note that `undefined` is used as an --- argument to `Field`. This argument is never used, so `undefined` will never --- throw an error. +-- | `Battery` instance for `Typed`. instance Typed Battery where - typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + typeOf = typeOfDefault spec :: Spec spec = do diff --git a/copilot/examples/StructsUpdateField.hs b/copilot/examples/StructsUpdateField.hs index 33f5d319f..5b0c7a35d 100644 --- a/copilot/examples/StructsUpdateField.hs +++ b/copilot/examples/StructsUpdateField.hs @@ -3,6 +3,7 @@ -- copilot-c99. {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -13,6 +14,7 @@ import qualified Prelude as P import Control.Monad (void, forM_) import Data.Proxy (Proxy(..)) import Data.Type.Equality (TestEquality(..), (:~:)(..)) +import GHC.Generics (Generic) import GHC.TypeLits (sameSymbol) import Language.Copilot @@ -20,20 +22,22 @@ import Copilot.Compile.C99 -- | Definition for `Volts`. data Volts = Volts - { numVolts :: Field "numVolts" Word16 - , flag :: Field "flag" Bool - } + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + deriving Generic -- | `Struct` instance for `Volts`. instance Struct Volts where - typeName _ = "volts" - toValues volts = [ Value Word16 (numVolts volts) - , Value Bool (flag volts) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- In order to run struct updates (as used in the "equalityStructUpdate" -- trigger below) in the Copilot interpreter, we must implement the -- `updateField` method. To do so, we must check to see if the supplied -- `Value` has a `Field` with the same name and type as a field in `Volts`. + -- (Alternatively, we could define this as @updateField = updateFieldDefault@, + -- but we demonstrate how to manually implement it below for educational + -- purposes.) updateField volts (Value fieldTy (field :: Field fieldName a)) -- For each field in `Volts`, we must: -- @@ -67,23 +71,23 @@ instance Struct Volts where -- | `Volts` instance for `Typed`. instance Typed Volts where - typeOf = Struct (Volts (Field 0) (Field False)) + typeOf = typeOfDefault data Battery = Battery - { temp :: Field "temp" Word16 - , volts :: Field "volts" (Array 10 Volts) - , other :: Field "other" (Array 10 (Array 5 Word32)) - } + { temp :: Field "temp" Word16 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + deriving Generic -- | `Battery` instance for `Struct`. instance Struct Battery where - typeName _ = "battery" - toValues battery = [ Value typeOf (temp battery) - , Value typeOf (volts battery) - , Value typeOf (other battery) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- We implement `updateField` similarly to how we implement it in the - -- `Struct Volts` instance above. + -- `Struct Volts` instance above. (Alternatively, we could define this as + -- @updateField = updateFieldDefault@, but we demonstrate how to manually + -- implement it below for educational purposes.) updateField battery (Value fieldTy (field :: Field fieldName a)) | Just Refl <- sameSymbol (Proxy @fieldName) (Proxy @"temp") , Just Refl <- testEquality fieldTy Word16 @@ -103,11 +107,9 @@ instance Struct Battery where | otherwise = error $ "Unexpected field: " P.++ show field --- | `Battery` instance for `Typed`. Note that `undefined` is used as an --- argument to `Field`. This argument is never used, so `undefined` will never --- throw an error. +-- | `Battery` instance for `Typed`. instance Typed Battery where - typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + typeOf = typeOfDefault spec :: Spec spec = do diff --git a/copilot/examples/what4/Structs.hs b/copilot/examples/what4/Structs.hs index 6b53d1098..cff433c4e 100644 --- a/copilot/examples/what4/Structs.hs +++ b/copilot/examples/what4/Structs.hs @@ -2,28 +2,29 @@ -- structs and arrays. Particular focus is on nested structs. -- For general usage of structs, refer to the general structs example. -{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} module Main where import qualified Prelude as P import Control.Monad (void, forM_) +import GHC.Generics (Generic) import Language.Copilot import Copilot.Theorem.What4 -- | Definition for `Volts`. data Volts = Volts - { numVolts :: Field "numVolts" Word16 - , flag :: Field "flag" Bool - } + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + deriving Generic -- | `Struct` instance for `Volts`. instance Struct Volts where - typeName _ = "volts" - toValues volts = [ Value Word16 (numVolts volts) - , Value Bool (flag volts) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- Note that we do not implement `updateField` here. `updateField` is only -- needed to make updates to structs work in the Copilot interpreter, and we -- do not use the interpreter in this example. (See @@ -32,29 +33,25 @@ instance Struct Volts where -- | `Volts` instance for `Typed`. instance Typed Volts where - typeOf = Struct (Volts (Field 0) (Field False)) + typeOf = typeOfDefault data Battery = Battery - { temp :: Field "temp" Word16 - , volts :: Field "volts" (Array 10 Volts) - , other :: Field "other" (Array 10 (Array 5 Word32)) - } + { temp :: Field "temp" Word16 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + deriving Generic -- | `Battery` instance for `Struct`. instance Struct Battery where - typeName _ = "battery" - toValues battery = [ Value typeOf (temp battery) - , Value typeOf (volts battery) - , Value typeOf (other battery) - ] + typeName = typeNameDefault + toValues = toValuesDefault -- Note that we do not implement `updateField` here for the same reasons as in -- the `Struct Volts` instance above. --- | `Battery` instance for `Typed`. Note that `undefined` is used as an --- argument to `Field`. This argument is never used, so `undefined` will never --- throw an error. +-- | `Battery` instance for `Typed`. instance Typed Battery where - typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + typeOf = typeOfDefault spec :: Spec spec = do From 24201b5c114d181c94c005f18c6bad2314ab9a92 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 12 Nov 2024 11:54:13 -0500 Subject: [PATCH 138/483] copilot-core: Document changes in CHANGELOG. Refs #564. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index a642472c4..c5ec07d11 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,6 +1,7 @@ -2024-12-29 +2025-01-06 * Deprecate fields of Copilot.Core.Expr.UExpr. (#565) * Increase test coverage. (#555) + * Define generic implementations of Struct and Typed methods. (#564) 2024-11-07 * Version bump (4.1). (#561) From cbab0cca637753b68d0d775095ab97f5a87937e0 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 12 Nov 2024 11:55:11 -0500 Subject: [PATCH 139/483] copilot: Document changes in CHANGELOG. Refs #564. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 40e0bdc24..72b6730e9 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,6 @@ -2025-01-02 +2025-01-06 * Bump upper version constraint on filepath. (#570) + * Update struct examples to use generic method implementations. (#564) 2024-11-07 * Version bump (4.1). (#561) From d44bebd29df6c0e348169f7b10a5b1980758fd49 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jan 2025 19:39:12 +0000 Subject: [PATCH 140/483] Bump versions and constraints. Refs #577. --- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 4 ++-- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot/copilot.cabal | 14 +++++++------- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 09e987d81..a693b60f4 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.1 +version : 4.2 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.1 && < 4.2 + , copilot-core >= 4.2 && < 4.3 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 232145325..963097d1e 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.1 +version: 4.2 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 87e668aad..68a0316f9 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.1 +version: 4.2 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.1 && < 4.2 + copilot-core >= 4.2 && < 4.3 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 74d501421..5a6a8adb9 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.1 +version: 4.2 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.1 && < 4.2 - , copilot-interpreter >= 4.1 && < 4.2 - , copilot-theorem >= 4.1 && < 4.2 + , copilot-core >= 4.2 && < 4.3 + , copilot-interpreter >= 4.2 && < 4.3 + , copilot-theorem >= 4.2 && < 4.3 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 709d18236..9d6a52ae9 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.1 +version: 4.2 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.1 && < 4.2 + , copilot-language >= 4.2 && < 4.3 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 3d3371335..ab5e63283 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.1 +version: 4.2 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.1 && < 4.2 + copilot-core >= 4.2 && < 4.3 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 135d75d5a..e708c0a6c 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.1 +version : 4.2 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 4.1 && < 4.2 - , copilot-prettyprinter >= 4.1 && < 4.2 + , copilot-core >= 4.2 && < 4.3 + , copilot-prettyprinter >= 4.2 && < 4.3 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index f645bf43d..2c1082075 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.1 +version: 4.2 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.1 && < 4.2 - , copilot-theorem >= 4.1 && < 4.2 - , copilot-language >= 4.1 && < 4.2 - , copilot-libraries >= 4.1 && < 4.2 - , copilot-c99 >= 4.1 && < 4.2 - , copilot-prettyprinter >= 4.1 && < 4.2 + , copilot-core >= 4.2 && < 4.3 + , copilot-theorem >= 4.2 && < 4.3 + , copilot-language >= 4.2 && < 4.3 + , copilot-libraries >= 4.2 && < 4.3 + , copilot-c99 >= 4.2 && < 4.3 + , copilot-prettyprinter >= 4.2 && < 4.3 exposed-modules: Language.Copilot, Language.Copilot.Main From e2f3f03b409a0f5cbd621691a2f73c880b478979 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jan 2025 19:40:32 +0000 Subject: [PATCH 141/483] Document changes in CHANGELOG. Refs #577. --- copilot-c99/CHANGELOG | 3 ++- copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 3 ++- copilot-libraries/CHANGELOG | 3 ++- copilot-prettyprinter/CHANGELOG | 3 ++- copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 17 insertions(+), 7 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index dcd314cad..84254f6e6 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-02 +2025-01-07 + * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprType. (#565) * Allow using same trigger name in multiple declarations. (#296) * Bump upper version constraint on filepath. (#570) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index c5ec07d11..d00518a78 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-06 +2025-01-07 + * Version bump (4.2). (#577) * Deprecate fields of Copilot.Core.Expr.UExpr. (#565) * Increase test coverage. (#555) * Define generic implementations of Struct and Typed methods. (#564) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index bd2aadc09..46c8f0c6b 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-07 + * Version bump (4.2). (#577) + 2024-11-07 * Version bump (4.1). (#561) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 64fe1728c..0968f9f1f 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-02 +2025-01-07 + * Version bump (4.2). (#577) * Bump upper version constraint on containers. (#570) 2024-11-07 diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 4a7ed2674..bd1a6b88c 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-02 +2025-01-07 + * Version bump (4.2). (#577) * Bump upper version constraint on containers. (#570) 2024-11-07 diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 6ea452f0e..b51261ae8 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,4 +1,5 @@ -2024-11-15 +2025-01-07 + * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprExpr. (#565) 2024-11-07 diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index e17fe667f..cf46bf03d 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-02 +2025-01-07 + * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprType,uExprExpr. (#565) * Bump upper constraint on containers, data-default. (#570) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 72b6730e9..f317d91a9 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2025-01-06 +2025-01-07 + * Version bump (4.2). (#577) * Bump upper version constraint on filepath. (#570) * Update struct examples to use generic method implementations. (#564) From f652dead7772761d7dfbc9ddbaf2a1968191a269 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 25 Jan 2025 17:13:07 +0000 Subject: [PATCH 142/483] copilot-theorem: Fix multiple typos in README. Refs #560. The README in copilot-theorem includes several typos, such as proportionnal, occurences and latters. Those typos should be corrected. This commit fixes the typos found in the README. Improving the style of the writing or formatting the code is not included as part of the fix. --- copilot-theorem/README.md | 147 +++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 73 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index e16d7f1ea..64cce8995 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -4,7 +4,7 @@ Highly automated proof techniques are a necessary step for the widespread adoption of formal methods in the software industry. Moreover, it could provide -a partial answer to one of its main issue which is scalability. +a partial answer to one of its main issues, which is scalability. *copilot-theorem* is a Copilot library aimed at checking automatically some safety properties on Copilot programs. It includes: @@ -17,9 +17,9 @@ properties on Copilot programs. It includes: proving basic k-inductive properties and for pedagogical purposes. * A prover producing native inputs for the *Kind2* model checker, developed at - University of Iowa. The latter uses both the *k-induction algorithm* extended - with *path compression* and *structural abstraction* [2] and the **IC3 - algorithm** with counterexample generalization based on *approximate + the University of Iowa. The latter uses both the *k-induction algorithm* + extended with *path compression* and *structural abstraction* [2] and the + **IC3 algorithm** with counterexample generalization based on *approximate quantifier elimination* [3]. ## A Tutorial @@ -39,12 +39,12 @@ README. ### First steps *copilot-theorem* is aimed at checking **safety properties** on Copilot programs. -Intuitively, a safety property is a property which express the idea that +Intuitively, a safety property is a property that expresses the idea that *nothing bad can happen*. In particular, any invalid safety property can be disproved by a finite execution trace of the program called a **counterexample**. Safety properties are often opposed to **liveness** properties, which express the idea that *something good will eventually -happen*. The latters are out of the scope of *copilot-theorem*. +happen*. The latter is out of the scope of *copilot-theorem*. Safety properties are simply expressed with standard boolean streams. In addition to triggers and observers declarations, it is possible to bind a @@ -70,7 +70,7 @@ This function takes three arguments: * The name of the proposition we want to check. * A strategy to prove the proposition. -In this case the proposition is simple enough so that we can check it directly +In this case, the proposition is simple enough so that we can check it directly by k-induction using `kind2Prover`. Therefore we can just write: ```haskell @@ -79,14 +79,15 @@ main = do prove spec' "gt0" (tell [Check $ kind2Prover def]) ``` -where `kind2Prover def` stands for the *Kind2 prover* with default +where `kind2Prover def` stands for the *Kind2 prover* with the default configuration. #### The Kind2 prover -The *Kind2* prover uses the model checker with the same name, from Iowa -university. It translates the Copilot specification into a *modular transition -system* (the Kind2 native format) and then calls the `kind2` executable. +The *Kind2* prover uses the model checker with the same name, from the +University of Iowa. It translates the Copilot specification into a *modular +transition system* (the Kind2 native format) and then calls the `kind2` +executable. It is provided by the `Copilot.Theorem.Kind2` module, which exports a `kind2Prover :: Options -> Prover` where the `Options` type is defined as @@ -117,7 +118,7 @@ Some examples are in the *examples* folder, including: ### An introduction to SMT-based model checking An introduction to the model-checking techniques used by *copilot-theorem* can be -found in the `doc` folder of this repository. It consists in a self sufficient +found in the `doc` folder of this repository. It consists of a self-sufficient set of slides. You can find some additional readings in the *References* section. @@ -216,16 +217,16 @@ process* [7] and so we have no way to keep them. #### Types In these three formats, GADTs are used to statically ensure a part of the -type-corectness of the specification, in the same spirit it is done in the -other Copilot libraries. *copilot-theorem* handles only three types which are -`Integer`, `Real` and `Bool` and which are handled by the SMTLib standard. +type-correctness of the specification, in the same spirit as in +other Copilot libraries. *copilot-theorem* works with only three types, +`Integer`, `Real` and `Bool`, all of which SMT-Lib can handle. *copilot-theorem* works with *pure* reals and integers. Thus, it is unsafe in the sense it ignores integer overflow problems and the loss of precision due to floating point arithmetic. #### The Kind2 prover -The *Kind2 prover* first translates the copilot specification into a *modular +The *Kind2 prover* first translates the Copilot specification into a *modular transition system*. Then, a chain of transformations is applied to this system (for instance, in order to remove dependency cycles among nodes). After this, the system is translated into the *Kind2 native format* and the `kind2` @@ -234,7 +235,7 @@ this process. ##### Modular transition systems -Let's look at the definition of a *modular transition systems*, in the +Let's look at the definition of a *modular transition system*, in the `TransSys.Spec` module: ```haskell @@ -295,10 +296,10 @@ types of variables: ##### The translation process -First, a copilot specification is translated into a modular transition system. +First, a Copilot specification is translated into a modular transition system. This process is defined in the `TransSys.Translate` module. Each stream is -associated to a node. The most significant task of this translation process is -to *flatten* the copilot specification so the value of all streams at time *n* +associated with a node. The most significant task of this translation process is +to *flatten* the Copilot specification so the value of all streams at time *n* only depends on the values of all the streams at time *n - 1*, which is not the case in the `Fib` example shown earlier. This is done by a simple program transformation which turns this: @@ -337,13 +338,13 @@ consistent. However, it can't be directly translated into the *Kind2 native file format*. Indeed, it is natural to bind each node to a predicate but the Kind2 file format requires that each predicate only uses previously defined predicates. However, some nodes in our transition system could be mutually -recursive. Therefore, the goal of the `removeCycles :: Spec -> Spec` function -defined in `TransSys.Transform` is to remove such dependency cycles. +recursive. Therefore, the goal of the `removeCycles :: Spec -> Spec` function, +defined in `TransSys.Transform`, is to remove such dependency cycles. -This function relies on the `mergeNodes :: [NodeId] -> Spec -> Spec` function -which signature is self-explicit. The latter solves name conflicts by using the -`Misc.Renaming` monad. Some code complexity has been added so the variable -names remains as clear as possible after merging two nodes. +This function relies on the `mergeNodes :: [NodeId] -> Spec -> Spec` function, +whose signature is self-explicit. The latter solves name conflicts by using the +`Misc.Renaming` monad. Some code complexity has been added so variable names +remain as clear as possible after merging two nodes. The function `removeCycles` computes the strongly connected components of the dependency graph and merge each one into a single node. The complexity of this @@ -354,7 +355,7 @@ merged in most practical cases. After the cycles have been removed, it is useful to apply another transformation which makes the translation from `TransSys.Spec` to `Kind2.AST` easier. This transformation is implemented in the `complete` function. In a -nutshell, it transforms a system such that +nutshell, it transforms a system such that: * If a node depends on another, it imports *all* its variables. * The dependency graph is transitive, that is if *A* depends of *B* which @@ -374,7 +375,7 @@ inline spec = mergeNodes [nodeId n | n <- specNodes spec] spec which discards all the structure of a *modular transition system* and turns it into a *non-modular transition system* with only one node. In fact, when -translating a copilot specification to a kind2 file, two styles are available: +translating a Copilot specification to a Kind2 file, two styles are available: the `Kind2.toKind2` function takes a `Style` argument which can take the value `Inlined` or `Modular`. The only difference is that in the first case, a call to `removeCycles` is replaced by a call to `inline`. @@ -382,17 +383,17 @@ to `removeCycles` is replaced by a call to `inline`. ### Limitations of copilot-theorem Now, we will discuss some limitations of the *copilot-theorem* tool. These -limitations are organized in two categories: the limitations related to the -Copilot language itself and its implementation, and the limitations related to -the model-checking techniques we are using. +limitations are split into two categories: limitations related to the Copilot +language itself and its implementation, and limitations related to the +model-checking techniques we are using. #### Limitations related to Copilot implementation -The reification process used to build the `Core.Spec` object looses many -informations about the structure of the original Copilot program. In fact, a -stream is kept in the reified program only if it is recursively defined. -Otherwise, all its occurences will be inlined. Moreover, let's look at the -`intCounter` function defined in the example `Grey.hs`: +The reification process used to build the `Core.Spec` object loses information +about the structure of the original Copilot program. In fact, a stream is kept +in the reified program only if it is recursively defined. Otherwise, all its +occurrences will be inlined. Moreover, let's look at the `intCounter` function +defined in the example `Grey.hs`: ```haskell intCounter :: Stream Bool -> Stream Word64 @@ -413,8 +414,8 @@ There are many problems with this: * It makes the inputs given to the SMT solvers larger and repetitive. We can't rewrite the Copilot reification process in order to avoid these -inconvenients as these informations are lost by GHC itself before it occurs. -The only solution we can see would be to use *Template Haskell* to generate +inconveniences as this information is lost by GHC itself before it occurs. The +only solution we can see would be to use *Template Haskell* to generate automatically some structural annotations, which might not be worth the dirt introduced. @@ -423,7 +424,7 @@ introduced. ##### Limitations of the IC3 algorithm The IC3 algorithm was shown to be a very powerful tool for hardware -certification. However, the problems encountered when verifying softwares are +certification. However, the problems encountered when verifying software are much more complex. For now, very few non-inductive properties can be proved by *Kind2* when basic integer arithmetic is involved. @@ -433,32 +434,32 @@ the inductiveness* (CTI) for a property, these techniques are used to find a lemma discarding it which is general enough so that all CTIs can be discarded in a finite number of steps. -The lemmas found by the current version fo *Kind2* are often too weak. Some +The lemmas found by the current version of *Kind2* are often too weak. Some suggestions to enhance this are presented in [1]. We hope some progress will be -made in this area in a near future. +made in this area in the near future. -A workaround to this problem would be to write kind of an interactive mode -where the user is invited to provide some additional lemmas when automatic -techniques fail. Another solution would be to make the properties being checked +A workaround to this problem would be to write an interactive mode where the +user is invited to provide some additional lemmas when automatic techniques +fail. Another solution would be to make the properties being checked quasi-inductive by hand. In this case, *copilot-theorem* is still a useful tool (especially for finding bugs) but the verification of a program can be long and -requires a high level of technicity. +requires a high level of technical knowledge. ##### Limitations related to the SMT solvers -The use of SMT solvers introduces two kind of limitations: +The use of SMT solvers introduces two kinds of limitations: 1. We are limited by the computing power needed by the SMT solvers 2. SMT solvers can't handle quantifiers efficiently Let's consider the first point. SMT solving is costly and its performances are sometimes unpredictable. For instance, when running the `SerialBoyerMoore` -example with the *k-induction prover*, Yices2 does not terminate. However, the *z3* +example with the *k-induction prover*, Yices2 does not terminate. However, the *Z3* SMT solver used by *Kind2* solves the problem instantaneously. Note that this performance gap is not due to the use of the IC3 algorithm because the property to check is inductive. It could be related to the fact the SMT problem produced by the *k-induction prover* uses uninterpreted functions for encoding streams instead -of simple integer variables, which is the case when the copilot program is +of simple integer variables, which is the case when the Copilot program is translated into a transition system. However, this wouldn't explain why the *k-induction prover* still terminates instantaneously on the `BoyerMoore` example, which seems not simpler by far. @@ -472,10 +473,10 @@ property. This fact could be used to enhance the proof scheme system (see the *Future work* section). However, this trick is not always possible. For instance, in the `SerialBoyerMoore` example, the property being checked should be quantified over all integer constants. Here, we can't just introduce an -arbitrary constant stream because it is the quantified property which is +arbitrary constant stream because it is the quantified property that is inductive and not the property specialized for a given constant stream. That's -why we have no other solution than replacing universal quantification by -*bounded* universal quantification by assuming all the elements of the input +why we have no other solution than replacing universal quantification with +*bounded* universal quantification, assuming all the elements of the input stream are in the finite list `allowed` and using the function `forAllCst`: ```haskell @@ -487,13 +488,13 @@ forAllCst l f = conj $ map (f . constant) l ``` However, this solution isn't completely satisfying because the size of the -property generated is proportionnal to the cardinal of `allowed`. +property generated is proportional to the length of `allowed`. #### Some scalability issues -A standard way to prove large programs is to rely on its logical structure by -writing a specification for each of its functions. This very natural approach -is hard to follow in our case because of +A standard way to prove large programs is to rely on their logical structure, +by writing a specification for each of their functions. This very natural +approach is hard to follow in our case due to: * The difficulty to deal with universal quantification. * The lack of *true* functions in Copilot: the latter offers metaprogramming @@ -503,7 +504,7 @@ is hard to follow in our case because of Once again, *copilot-theorem* is still a very useful tool, especially for debugging purposes. However, we don't think it is adapted to write and check a -complete specification for large scale programs. +complete specification for large-scale programs. ## Future work @@ -512,19 +513,19 @@ complete specification for large scale programs. These features are not currently provided due to the lack of important features in the Kind2 SMT solver. -#### Counterexamples displaying +#### Displaying counterexamples Counterexamples are not displayed with the Kind2 prover because Kind2 doesn't support XML output of counterexamples. If the last feature is provided, it -should be easy to implement counterexamples displaying in *copilot-theorem*. For -this, we recommend to keep some informations about *observers* in +should be easy to implement displaying of counterexamples in *copilot-theorem*. +For this, we recommend keeping some information about *observers* in `TransSys.Spec` and to add one variable per observer in the Kind2 output file. #### Bad handling of non-linear operators and external functions Non-linear Copilot operators and external functions are poorly handled because -of the lack of support of uninterpreted functions in the Kind2 native format. A -good way to handle these would be to use uninterpreted functions. With this +of the lack of support for uninterpreted functions in the Kind2 native format. +A good way to handle these would be to use uninterpreted functions. With this solution, properties like ```haskell 2 * sin x + 1 <= 3 @@ -533,7 +534,7 @@ with `x` any stream can't be proven but at least the following can be proved ```haskell let y = x in sin x == sin y ``` -Currently, the *Kind2 prover* fail with the last example, as the results of +Currently, the *Kind2 prover* fails with the last example, as the results of unknown functions are turned into fresh unconstrained variables. ### Simple extensions @@ -542,7 +543,7 @@ The following extensions would be really simple to implement given the current architecture of Kind2. + If inductive proving of a property fails, giving the user a concrete CTI - (*Counterexample To The Inductiveness*, see the [1]). + (*Counterexample To The Inductiveness*, see [1]). + Use Template Haskell to declare automatically some observers with the same names used in the original program. @@ -559,29 +560,29 @@ architecture of Kind2. - Declare assumptions and invariants next to the associated code instead of gathering all properties in a single place. - - Declare a frequent code pattern which should be factorized in the - transition problem (see the section about Copilot limitations) + - Declare a frequent code pattern that can be factorized in the transition + problem (see the section about Copilot limitations) ## FAQ -### Why does the code related to transition systems look so complex ? +### Why does the code related to transition systems look so complex? -It is true the code of `TransSys` is quite complex. In fact, it would be really -straightforward to produce a flattened transition system and then a Kind2 file -with just a single *top* predicate. In fact, It would be as easy as producing -an *IL* specification. +It is true that the code of `TransSys` is quite complex. In fact, it would be +really straightforward to produce a flattened transition system and then a +Kind2 file with just a single *top* predicate. In fact, It would be as easy as +producing an *IL* specification. To be honest, I'm not sure producing a modular *Kind2* output is worth the complexity added. It's especially true at the time I write this in the sense that: * Each predicate introduced is used only one time (which is true because - copilot doesn't handle functions or parametrized streams like Lustre does and - everything is inlined during the reification process). + Copilot doesn't handle functions or parameterized streams like Lustre does + and everything is inlined during the reification process). * A similar form of structure could be obtained from a flattened Kind2 native input file with some basic static analysis by producing a dependency graph between variables. -* For now, the *Kind2* model-checker ignores these structure informations. +* For now, the *Kind2* model-checker ignores these structures. However, the current code offers some nice transformation tools (node merging, `Renaming` monad...) which could be useful if you intend to write a tool for From 2d7a06e3a16c6cbd0a2b43c4213d60112ea9bd44 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 25 Jan 2025 17:15:51 +0000 Subject: [PATCH 143/483] copilot-theorem: Document changes in CHANGELOG. Refs #560. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index cf46bf03d..a130e6711 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-25 + * Fix multiple typos in README. (#560) + 2025-01-07 * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprType,uExprExpr. (#565) From 7e8829b1bacb82855f2e00a9bbae1755773a3d88 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:57:07 +0000 Subject: [PATCH 144/483] copilot-core: Fix typo in documentation. Refs #587. This commit fixes a typo (a sentence ending in double period) in the documentation of `copilot-core`. --- copilot-core/src/Copilot/Core/Spec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/src/Copilot/Core/Spec.hs b/copilot-core/src/Copilot/Core/Spec.hs index 534caf927..7a71c780e 100644 --- a/copilot-core/src/Copilot/Core/Spec.hs +++ b/copilot-core/src/Copilot/Core/Spec.hs @@ -13,7 +13,7 @@ -- -- In order to be executed, high-level Copilot Language Spec must be turned -- into Copilot Core's 'Spec'. This module defines the low-level Copilot Core --- representations for Specs and the main types of element in a spec.. +-- representations for Specs and the main types of element in a spec. module Copilot.Core.Spec ( Stream (..) , Observer (..) From 6bfb6a197102aa8fabf8c9485a0fadbf2a64b168 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:57:25 +0000 Subject: [PATCH 145/483] copilot-theorem: Fix typo in documentation. Refs #587. This commit fixes a typo (a sentence ending in double period) in the documentation of `copilot-theorem`. --- copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs b/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs index 102ff6221..f828850b2 100644 --- a/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs +++ b/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs @@ -31,7 +31,7 @@ singleton a = List [Atom a] -- (s) list = List -- (ss) -- | Sequence of expressions with a root or main note, and a series of --- additional expressions or arguments.. +-- additional expressions or arguments. node a l = List (Atom a : l) -- (s ss) -- A straightforward string representation for 'SExpr's of Strings that From 751767d2a5cfe5bc9ad8caf6574e3bfa1cfd9d1f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:57:45 +0000 Subject: [PATCH 146/483] copilot-language: Fix typo in documentation. Refs #587. This commit fixes a typo (a sentence ending in double period) in the documentation of `copilot-language`. --- copilot-language/src/Copilot/Language/Operators/Projection.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-language/src/Copilot/Language/Operators/Projection.hs b/copilot-language/src/Copilot/Language/Operators/Projection.hs index a350e9593..0d05c050f 100644 --- a/copilot-language/src/Copilot/Language/Operators/Projection.hs +++ b/copilot-language/src/Copilot/Language/Operators/Projection.hs @@ -23,7 +23,7 @@ infixl 8 =$ -- | Common interface to manipulate portions of a larger data structure. -- -- A projectable d s t means that it is possible to manipulate a sub-element s --- of type t carried in a stream of type d.. +-- of type t carried in a stream of type d. class Projectable d s t | d s -> t where -- | Unapplied projection or element access on a type. From 171e5f6be374efa56da95a0612ec4448d7b2a1de Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:58:35 +0000 Subject: [PATCH 147/483] copilot-core: Document changes in CHANGELOG. Refs #587. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index d00518a78..e51b49366 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-28 + * Fix typo in documentation. (#587) + 2025-01-07 * Version bump (4.2). (#577) * Deprecate fields of Copilot.Core.Expr.UExpr. (#565) From 3233cafa323be8529fa661fc9a0a3460a951d68d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:58:56 +0000 Subject: [PATCH 148/483] copilot-theorem: Document changes in CHANGELOG. Refs #587. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index a130e6711..4251f8875 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ -2025-01-25 +2025-01-28 * Fix multiple typos in README. (#560) + * Fix typo in documentation. (#587) 2025-01-07 * Version bump (4.2). (#577) From 0d9214e57b725cd0d9823aeb7bff360d9463a58d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 29 Jan 2025 01:59:09 +0000 Subject: [PATCH 149/483] copilot-language: Document changes in CHANGELOG. Refs #587. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 0968f9f1f..f4668a9e1 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-28 + * Fix typo in documentation. (#587) + 2025-01-07 * Version bump (4.2). (#577) * Bump upper version constraint on containers. (#570) From 03a80598d9a0b9e5df4a36bfd7fd2a71c1490f59 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 31 Jan 2025 05:02:51 +0000 Subject: [PATCH 150/483] copilot: Include missing dependencies in installation instructions. Refs #591. The Linux section of the README lists GHC and Cabal as dependencies, but more packages are needed. Specifically, in a fresh new Ubuntu as provided via the Docker Hub, users also need to install libz-dev and pkg-config. The programs alex and happy also need to be available. This commit adjust the installation instructions for Linux distributions where Copilot is not directly available as a package to include other dependencies needed, namely libz-dev, pkg-config, alex and happy. The text prior to the instructions is also updated to reflect that this is no longer just the Haskell toolchain but, rather, all dependencies of Copilot. --- copilot/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot/README.md b/copilot/README.md index 4fb3180d4..c03ccfbce 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -102,10 +102,10 @@ Copilot you must install a Haskell compiler (GHC) and the package manager Cabal. We currently support all versions of GHC from 8.6.5 to modern versions (9.8 as of this writing). You can install the toolchain using [ghcup](https://www.haskell.org/ghcup/) or, if you are on Debian/Ubuntu, -directly with `apt-get`: +you can use `apt-get` to install all dependencies as follows: ```sh -$ sudo apt-get install ghc cabal-install +$ sudo apt-get install ghc cabal-install alex happy pkg-config libz-dev ``` Once the compiler is installed, install Copilot from From b3bc4e43331ef0301b06d2aa5233f758dee4df87 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 31 Jan 2025 05:05:42 +0000 Subject: [PATCH 151/483] copilot: Document changes in CHANGELOG. Refs #591. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index f317d91a9..d9994c28d 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-01-30 + * Include missing dependencies in installation instructions. (#591) + 2025-01-07 * Version bump (4.2). (#577) * Bump upper version constraint on filepath. (#570) From 565d016a969b567156b1a7305f9c03ad80bb0acf Mon Sep 17 00:00:00 2001 From: Esther Conrad Date: Fri, 31 Jan 2025 13:45:45 -0500 Subject: [PATCH 152/483] copilot: Update version of GHC in README. Refs #590. Copilot is currently able to work with GHC 9.10, but the README still indicates that it is compatible with versions up to 9.8. This commit updates the installation instructions to reflect that Copilot is compatible with versions of GHC 9.10 as of this writing. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index c03ccfbce..7ce1e482e 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -100,7 +100,7 @@ ghci> ghci> Leaving GHCi. On other Linux distributions or older Debian-based distributions, to use Copilot you must install a Haskell compiler (GHC) and the package manager Cabal. We currently support all versions of GHC from 8.6.5 to modern versions -(9.8 as of this writing). You can install the toolchain using +(9.10 as of this writing). You can install the toolchain using [ghcup](https://www.haskell.org/ghcup/) or, if you are on Debian/Ubuntu, you can use `apt-get` to install all dependencies as follows: From c1829da235e930760f1958804ec1b6140c7710ae Mon Sep 17 00:00:00 2001 From: Esther Conrad Date: Fri, 31 Jan 2025 13:52:48 -0500 Subject: [PATCH 153/483] copilot: Document changes in CHANGELOG. Refs #590. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index d9994c28d..d6574cd3e 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,6 @@ -2025-01-30 +2025-01-31 * Include missing dependencies in installation instructions. (#591) + * Update version of GHC in README. (#590) 2025-01-07 * Version bump (4.2). (#577) From 0bd29d5ab0de07a2a7e4f7bf30687ff73343fe77 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 11:20:16 -0500 Subject: [PATCH 154/483] copilot-core: Add Show instance for Type. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. To define a companion function to `prove` that also returns counterexample information upon a failed proof, it is convenient to be able to display `Type` information in panic messages. This commit derives a basic `Show` instance for `Type` so that `copilot-theorem` can display them whenever an internal invariant is violated. --- copilot-core/src/Copilot/Core/Type.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 814a22c75..8d54c82f3 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -5,6 +5,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} @@ -154,6 +155,7 @@ data Type :: * -> * where , Typed t ) => Type t -> Type (Array n t) Struct :: (Typed a, Struct a) => a -> Type a +deriving instance Show (Type a) -- | Return the length of an array from its type typeLength :: forall n t . KnownNat n => Type (Array n t) -> Int From 86d75431c1552dedcd435e8e0efc345d228bc3d6 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 11:18:05 -0500 Subject: [PATCH 155/483] copilot-theorem: Record Typed evidence in XEmptyArray and XArray. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. To update the `valFromExpr` function in order to produce concrete array values for counterexample purposes, we need to call the `Array` data constructor, which has a `Typed` constraint. However, the `XEmptyArray` and `XArray` data constructors do not record evidence that their array element types were instances of the `Typed` class, which makes it impossible to use them in `valFromExpr`. This commit adds the necessary constraints to each data constructor to make their array elements `Typed`. --- .../src/Copilot/Theorem/What4/Translate.hs | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs index f6ced33b7..cef4829ee 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs @@ -56,7 +56,7 @@ import Data.Parameterized.NatRepr (LeqProof (..), NatCases (..), intValue, isZeroOrGT1, knownNat, minusPlusCancel, mkNatRepr, testNatCases, - testStrictLeq) + testStrictLeq, withKnownNat) import Data.Parameterized.Some (Some (..)) import Data.Parameterized.SymbolRepr (SymbolRepr, knownSymbol) import qualified Data.Parameterized.Vector as V @@ -370,10 +370,10 @@ translateConstExpr sym tp a = case tp of elts <- traverse (translateConstExpr sym tp) (CT.arrayElems a) Some n <- return $ mkNatRepr (genericLength elts) case isZeroOrGT1 n of - Left Refl -> return XEmptyArray + Left Refl -> return $ XEmptyArray tp Right LeqProof -> do let Just v = V.fromList n elts - return $ XArray v + return $ withKnownNat n $ XArray v CT.Struct _ -> do elts <- forM (CT.toValues a) $ \(CT.Value tp (CT.Field a)) -> translateConstExpr sym tp a @@ -409,7 +409,7 @@ freshCPConstant sym nm tp = case tp of atp@(CT.Array itp) -> do let n = arrayLen atp case isZeroOrGT1 n of - Left Refl -> return XEmptyArray + Left Refl -> return $ XEmptyArray itp Right LeqProof -> do Refl <- return $ minusPlusCancel n (knownNat @1) elts :: V.Vector n (XExpr t) <- @@ -1281,7 +1281,13 @@ mkIte sym pred xe1 xe2 = case (xe1, xe2) of XDouble <$> WFP.iFloatIte @_ @WFP.DoubleFloat sym pred e1 e2 (XStruct xes1, XStruct xes2) -> XStruct <$> zipWithM (mkIte sym pred) xes1 xes2 - (XEmptyArray, XEmptyArray) -> return XEmptyArray + (XEmptyArray tp1, XEmptyArray tp2) -> + case tp1 `testEquality` tp2 of + Just Refl -> return (XEmptyArray tp1) + Nothing -> panic [ "Element type mismatch in ite" + , show tp1 + , show tp2 + ] (XArray xes1, XArray xes2) -> case V.length xes1 `testEquality` V.length xes2 of Just Refl -> XArray <$> V.zipWithM (mkIte sym pred) xes1 xes2 @@ -1438,25 +1444,34 @@ data XExpr sym where sym (WFP.SymInterpretedFloatType sym WFP.DoubleFloat) -> XExpr sym - XEmptyArray :: XExpr sym - XArray :: 1 <= n => V.Vector n (XExpr sym) -> XExpr sym + + -- | An empty array. The 'CT.Typed' constraint and accompanying 'CT.Type' + -- field are necessary in order to record evidence that the array type can be + -- used in a context where 'CT.Typed' is required. + XEmptyArray :: CT.Typed t => CT.Type t -> XExpr sym + + -- | A non-empty array. The 'KnownNat' constraint is necessary in order to + -- record evidence that the array type can be used in a context for 'CT.Typed' + -- is required. + XArray :: (KnownNat n, 1 <= n) => V.Vector n (XExpr sym) -> XExpr sym + XStruct :: [XExpr sym] -> XExpr sym instance WI.IsExprBuilder sym => Show (XExpr sym) where - show (XBool e) = "XBool " ++ show (WI.printSymExpr e) - show (XInt8 e) = "XInt8 " ++ show (WI.printSymExpr e) - show (XInt16 e) = "XInt16 " ++ show (WI.printSymExpr e) - show (XInt32 e) = "XInt32 " ++ show (WI.printSymExpr e) - show (XInt64 e) = "XInt64 " ++ show (WI.printSymExpr e) - show (XWord8 e) = "XWord8 " ++ show (WI.printSymExpr e) - show (XWord16 e) = "XWord16 " ++ show (WI.printSymExpr e) - show (XWord32 e) = "XWord32 " ++ show (WI.printSymExpr e) - show (XWord64 e) = "XWord64 " ++ show (WI.printSymExpr e) - show (XFloat e) = "XFloat " ++ show (WI.printSymExpr e) - show (XDouble e) = "XDouble " ++ show (WI.printSymExpr e) - show XEmptyArray = "[]" - show (XArray vs) = showList (V.toList vs) "" - show (XStruct xs) = "XStruct " ++ showList xs "" + show (XBool e) = "XBool " ++ show (WI.printSymExpr e) + show (XInt8 e) = "XInt8 " ++ show (WI.printSymExpr e) + show (XInt16 e) = "XInt16 " ++ show (WI.printSymExpr e) + show (XInt32 e) = "XInt32 " ++ show (WI.printSymExpr e) + show (XInt64 e) = "XInt64 " ++ show (WI.printSymExpr e) + show (XWord8 e) = "XWord8 " ++ show (WI.printSymExpr e) + show (XWord16 e) = "XWord16 " ++ show (WI.printSymExpr e) + show (XWord32 e) = "XWord32 " ++ show (WI.printSymExpr e) + show (XWord64 e) = "XWord64 " ++ show (WI.printSymExpr e) + show (XFloat e) = "XFloat " ++ show (WI.printSymExpr e) + show (XDouble e) = "XDouble " ++ show (WI.printSymExpr e) + show (XEmptyArray _) = "[]" + show (XArray vs) = showList (V.toList vs) "" + show (XStruct xs) = "XStruct " ++ showList xs "" -- * Stream offsets From e23df09d3c8b2312269a4824c35ed0900dec4b30 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 11:36:21 -0500 Subject: [PATCH 156/483] copilot-theorem: Add XEmptyArray and XArray cases for valFromExpr. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. The `valFromExpr` function (which produces concrete values when making a counterexample) was lacking cases for `XEmptyArray` and `XArray`, so it would fail if the function was called on these values. This commit adds these missing cases, which make use of the `Typed` evidence added to `XEmptyArray` and `XArray` in a previous commit. We do not yet add a case for structs, which prove more challenging. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 36 ++++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 960df8d93..76ebd90f6 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -45,9 +45,10 @@ module Copilot.Theorem.What4 , XExpr(..) ) where -import qualified Copilot.Core.Expr as CE -import qualified Copilot.Core.Spec as CS -import qualified Copilot.Core.Type as CT +import qualified Copilot.Core.Expr as CE +import qualified Copilot.Core.Spec as CS +import qualified Copilot.Core.Type as CT +import qualified Copilot.Core.Type.Array as CTA import qualified What4.Config as WC import qualified What4.Expr.Builder as WB @@ -66,6 +67,7 @@ import qualified Data.Map as Map import Data.Parameterized.NatRepr import Data.Parameterized.Nonce import Data.Parameterized.Some +import qualified Data.Parameterized.Vector as V import GHC.Float (castWord32ToFloat, castWord64ToDouble) import LibBF (BigFloat, bfToDouble, pattern NearEven) import qualified Panic as Panic @@ -387,10 +389,14 @@ expectedBool :: forall m sym a. expectedBool what xe = panic [what ++ " expected to have boolean result", show xe] -data CopilotValue a = CopilotValue { cvType :: CT.Type a - , cvVal :: a - } +-- | A Copilot value paired with its 'CT.Type'. +data CopilotValue a where + CopilotValue :: CT.Typed a => CT.Type a -> a -> CopilotValue a +-- | Convert a symbolic 'XExpr' into a concrete 'CopilotValue'. +-- +-- Struct values are not currently supported, so attempting to convert an +-- 'XStruct' value will raise an error. valFromExpr :: forall sym t st fm. ( sym ~ WB.ExprBuilder t st (WB.Flags fm) , WI.KnownRepr WB.FloatModeRepr fm @@ -420,7 +426,23 @@ valFromExpr ge xe = case xe of (fst . bfToDouble NearEven) fromRational (castWord64ToDouble . fromInteger . BV.asUnsigned) - _ -> error "valFromExpr unhandled case" + XEmptyArray tp -> + pure $ Some $ CopilotValue (CT.Array @0 tp) (CTA.array []) + XArray es -> do + (someCVs :: V.Vector n (Some CopilotValue)) <- traverse (valFromExpr ge) es + (Some (CopilotValue headTp _headVal), _) <- pure $ V.uncons someCVs + cvs <- + traverse + (\(Some (CopilotValue tp val)) -> + case tp `testEquality` headTp of + Just Refl -> pure val + Nothing -> panic [ "XArray with mismatched element types" + , show tp + , show headTp + ]) + someCVs + pure $ Some $ CopilotValue (CT.Array @n headTp) (CTA.array (V.toList cvs)) + XStruct _ -> error "valFromExpr: Structs not currently handled" where fromBV :: forall a w . Num a => BV.BV w -> a fromBV = fromInteger . BV.asUnsigned From c5ea1c0451b46157cf1c27e8f8607c7877f6ff8f Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 24 Feb 2025 16:00:49 -0500 Subject: [PATCH 157/483] copilot-theorem: Add function to produce counterexamples for invalid properties. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. This introduces a new `proveWithCounterExample` function to `Copilot.Theorem.What4` that mirrors the type signature of `prove`, except that it returns a variant of `SatResult` (`SatResultCex`) where the `Invalid` equivalent (`InvalidCex`) encodes counterexample information. `copilot-theorem` users can then interpret the results of the counterexample in Copilot specifications. As part of this commit, we change the definition of the `CounterExample` data type. This is safe to do, as `CounterExample` was completely unused prior to this commit, nor was it exported. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 269 +++++++++++++++++-- 1 file changed, 250 insertions(+), 19 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 76ebd90f6..6e3fd53a9 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -37,12 +37,17 @@ module Copilot.Theorem.What4 prove , Solver(..) , SatResult(..) + , proveWithCounterExample + , SatResultCex(..) + , CounterExample(..) -- * Bisimulation proofs about @copilot-c99@ code , computeBisimulationProofBundle , BisimulationProofBundle(..) , BisimulationProofState(..) -- * What4 representations of Copilot expressions , XExpr(..) + , CopilotValue(..) + , StreamOffset(..) ) where import qualified Copilot.Core.Expr as CE @@ -91,7 +96,160 @@ data Solver = CVC4 | DReal | Yices | Z3 data SatResult = Valid | Invalid | Unknown deriving Show -type CounterExample = [(String, Some CopilotValue)] +-- | The 'proveWithCounterExample' function returns results of this form for +-- each property in a spec. This is largely the same as 'SatResult', except that +-- 'InvalidCex' also records a 'CounterExample'. +data SatResultCex = ValidCex | InvalidCex CounterExample | UnknownCex + +-- | Concrete values that cause a property in a Copilot specification to be +-- invalid. As a simple example, consider the following spec: +-- +-- @ +-- spec :: Spec +-- spec = do +-- let s :: Stream Bool +-- s = [False] ++ constant True +-- void $ prop "should be invalid" (forAll s) +-- @ +-- +-- This defines a stream @s@ where the first value is @False@, but all +-- subsequent values are @True@'. This is used in a property that asserts that +-- the values in @s@ will be @True@ at all possible time steps. This is clearly +-- not true, given that @s@'s first value is @False@. As such, we would expect +-- that proving this property would yield an 'InvalidCex' result, where one of +-- the base cases would state that the @s@ stream contains a @False@ value. +-- +-- We can use the 'proveWithCounterExample' function to query an SMT solver to +-- compute a counterexample: +-- +-- @ +-- CounterExample +-- { 'baseCases' = +-- [False] +-- , 'inductionStep' = +-- True +-- , 'concreteExternVars' = +-- fromList [] +-- , 'concreteStreamValues' = +-- fromList +-- [ ( (0, 'AbsoluteOffset' 0), False ) +-- , ( (0, 'RelativeOffset' 0), False ) +-- , ( (0, 'RelativeOffset' 1), True ) +-- ] +-- } +-- @ +-- +-- Let's go over what this counterexample is saying: +-- +-- * The 'inductionStep' of the proof is 'True', so that part of the proof was +-- successful. On the other hand, the 'baseCases' contain a 'False', so the +-- proof was falsified when proving the base cases. (In this case, the list +-- has only one element, so there is only a single base case.) +-- +-- * 'concreteStreamValues' reports the SMT solver's concrete values for each +-- stream during relevant parts of the proof as a 'Map.Map'. +-- +-- The keys of the map are pairs. The first element of the pair is the stream +-- 'CE.Id', and in this example, the only 'CE.Id' is @0@, corresponding to the +-- stream @s@. The second element is the time offset. An 'AbsoluteOffset' +-- indicates an offset starting from the initial time step, and a +-- 'RelativeOffset' indicates an offset from an arbitrary point in time. +-- 'AbsoluteOffset's are used in the base cases of the proof, and +-- 'RelativeOffset's are used in the induction step of the proof. +-- +-- The part of the map that is most interesting to us is the +-- @( (0, 'AbsoluteOffset' 0), False )@ entry, which represents a base case +-- where there is a value of @False@ in the stream @s@ during the initial time +-- step. Sure enough, this is enough to falsify the property @forAll s@. +-- +-- * There are no extern streams in this example, so 'concreteExternVars' is +-- empty. +-- +-- We can also see an example of where a proof succeeds in the base cases, but +-- fails during the induction step: +-- +-- @ +-- spec :: Spec +-- spec = do +-- let t :: Stream Bool +-- t = [True] ++ constant False +-- void $ prop "should also be invalid" (forAll t) +-- @ +-- +-- With the @t@ stream above, the base cases will succeed +-- ('proveWithCounterExample' uses @k@-induction with a value of @k == 1@ in +-- this example, so there will only be a single base case). On the other hand, +-- the induction step will fail, as later values in the stream will be @False@. +-- If we try to 'proveWithCounterExample' this property, then it will fail with: +-- +-- @ +-- CounterExample +-- { 'baseCases' = +-- [True] +-- , 'inductionStep' = +-- False +-- , 'concreteExternVars' = +-- fromList [] +-- , 'concreteStreamValues' = +-- fromList +-- [ ( (0, 'AbsoluteOffset' 0), True ) +-- , ( (0, 'RelativeOffset' 0), True ) +-- , ( (0, 'RelativeOffset' 1), False ) +-- ] +-- } +-- @ +-- +-- This time, the 'inductionStep' is 'False'. If we look at the +-- 'concreteStreamValues', we see the values at @'RelativeOffset' 0@ and +-- @'RelativeOffset' 1@ (which are relevant to the induction step) are @True@ +-- and @False@, respectively. Since this is a proof by @k@-induction where +-- @k == 1@, the fact that the value at @'RelativeOffset 1@ is @False@ indicates +-- that the induction step was falsified. +-- +-- Note that this proof does not say /when/ exactly the time steps at +-- @'RelativeOffset' 0@ or @'RelativeOffset' 1@ occur, only that that will occur +-- relative to some arbitrary point in time. In this example, they occur +-- relative to the initial time step, so @'RelativeOffset' 1@ would occur at the +-- second time step overall. In general, however, these time steps may occur far +-- in the future, so it is possible that one would need to step through the +-- execution of the streams for quite some time before finding the +-- counterexample. +-- +-- Be aware that counterexamples involving struct values are not currently +-- supported. +data CounterExample = CounterExample + { -- | A list of base cases in the proof, where each entry in the list + -- corresponds to a particular time step. For instance, the first element + -- in the list corresponds to the initial time step, the second element in + -- the list corresponds to the second time step, and so on. A 'False' entry + -- anywhere in this list will cause the overall proof to be 'InvalidCex'. + -- + -- Because the proof uses @k@-induction, the number of base cases (i.e., the + -- number of entries in this list) is equal to the value of @k@, which is + -- chosen using heuristics. + baseCases :: [Bool] + -- | Whether the induction step of the proof was valid or not. That is, + -- given an arbitrary time step @n@, if the property is assumed to hold at + -- time steps @n@, @n+1@, ..., @n+k@, then this will be @True@ is the + -- property can the be proven to hold at time step @n+k+1@ (and 'False' + -- otherwise). If this is 'False', then the overall proof will be + -- 'InvalidCex'. + , inductionStep :: Bool + -- | The concrete values in the Copilot specification's extern streams that + -- lead to the property being invalid. + -- + -- Each key in the 'Map.Map' is the 'CE.Name' of an extern stream paired + -- with a 'StreamOffset' representing the time step. The key's corresponding + -- value is the concrete value of the extern stream at that time step. + , concreteExternValues :: Map.Map (CE.Name, StreamOffset) (Some CopilotValue) + -- | The concrete values in the Copilot specification's streams (excluding + -- extern streams) that lead to the property being invalid. + -- + -- Each key in the 'Map.Map' is the 'CE.Id' of a stream paired with a + -- 'StreamOffset' representing the time step. The key's corresponding value + -- is the concrete value of the extern stream at that time step. + , concreteStreamValues :: Map.Map (CE.Id, StreamOffset) (Some CopilotValue) + } -- | Attempt to prove all of the properties in a spec via an SMT solver (which -- must be installed locally on the host). Return an association list mapping @@ -101,7 +259,73 @@ prove :: Solver -> CS.Spec -- ^ Spec -> IO [(CE.Name, SatResult)] -prove solver spec = do +prove solver spec = proveInternal solver spec $ \_ _ _ satRes -> + case satRes of + WS.Sat _ -> pure Invalid + WS.Unsat _ -> pure Valid + WS.Unknown -> pure Unknown + +-- | Attempt to prove all of the properties in a spec via an SMT solver (which +-- must be installed locally on the host). Return an association list mapping +-- the names of each property to the result returned by the solver. +-- +-- Unlike 'prove', 'proveWithCounterExample' returns a 'SatResultCex'. This +-- means that if a result is invalid, then it will include a 'CounterExample' +-- which describes the circumstances under which the property was falsified. See +-- the Haddocks for 'CounterExample' for more details. +-- +-- Note that this function does not currently support creating counterexamples +-- involving struct values, so attempting to call 'proveWithCounterExample' on a +-- specification that uses structs will raise an error. +proveWithCounterExample :: Solver + -- ^ Solver to use + -> CS.Spec + -- ^ Spec + -> IO [(CE.Name, SatResultCex)] +proveWithCounterExample solver spec = + proveInternal solver spec $ \baseCases indStep st satRes -> + case satRes of + WS.Sat ge -> do + gBaseCases <- traverse (WG.groundEval ge) baseCases + gIndStep <- WG.groundEval ge indStep + gExternValues <- traverse (valFromExpr ge) (externVars st) + gStreamValues <- traverse (valFromExpr ge) (streamValues st) + let cex = CounterExample + { baseCases = gBaseCases + , inductionStep = gIndStep + , concreteExternValues = gExternValues + , concreteStreamValues = gStreamValues + } + pure (InvalidCex cex) + WS.Unsat _ -> pure ValidCex + WS.Unknown -> pure UnknownCex + +-- | Attempt to prove all of the properties in a spec via an SMT solver (which +-- must be installed locally on the host). For each 'WS.SatResult' returned by +-- the solver, pass it to a continuation along with the relevant parts of the +-- proof-related state. +-- +-- This is an internal-only function that is used to power 'prove' and +-- 'proveWithCounterExample'. +proveInternal :: Solver + -- ^ Solver to use + -> CS.Spec + -- ^ Spec + -> (forall sym t st fm + . ( sym ~ WB.ExprBuilder t st (WB.Flags fm) + , WI.KnownRepr WB.FloatModeRepr fm ) + => [WI.Pred sym] + -- The proof's base cases + -> WI.Pred sym + -- The proof's induction step + -> TransState sym + -- The proof state + -> WS.SatResult (WG.GroundEvalFn t) () + -- The overall result of the proof + -> IO a) + -- ^ Continuation to call on each solver result + -> IO [(CE.Name, a)] +proveInternal solver spec k = do -- Setup symbolic backend Some ng <- newIONonceGenerator sym <- WB.newExprBuilder WB.FloatIEEERepr EmptyState ng @@ -155,23 +379,30 @@ prove solver spec = do not_p <- liftIO $ WI.notPred sym p let clauses = [not_p] - case solver of - CVC4 -> liftIO $ WS.runCVC4InOverride sym WS.defaultLogData clauses $ \case - WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid) - WS.Unsat _ -> return (CS.propertyName pr, Valid) - WS.Unknown -> return (CS.propertyName pr, Unknown) - DReal -> liftIO $ WS.runDRealInOverride sym WS.defaultLogData clauses $ \case - WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid) - WS.Unsat _ -> return (CS.propertyName pr, Valid) - WS.Unknown -> return (CS.propertyName pr, Unknown) - Yices -> liftIO $ WS.runYicesInOverride sym WS.defaultLogData clauses $ \case - WS.Sat _ge -> return (CS.propertyName pr, Invalid) - WS.Unsat _ -> return (CS.propertyName pr, Valid) - WS.Unknown -> return (CS.propertyName pr, Unknown) - Z3 -> liftIO $ WS.runZ3InOverride sym WS.defaultLogData clauses $ \case - WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid) - WS.Unsat _ -> return (CS.propertyName pr, Valid) - WS.Unknown -> return (CS.propertyName pr, Unknown) + st <- get + let k' = k base_cases ind_case st + satRes <- + case solver of + CVC4 -> liftIO $ WS.runCVC4InOverride sym WS.defaultLogData clauses $ \case + WS.Sat (ge, _) -> k' (WS.Sat ge) + WS.Unsat x -> k' (WS.Unsat x) + WS.Unknown -> k' WS.Unknown + DReal -> liftIO $ WS.runDRealInOverride sym WS.defaultLogData clauses $ \case + WS.Sat (c, m) -> do + ge <- WS.getAvgBindings c m + k' (WS.Sat ge) + WS.Unsat x -> k' (WS.Unsat x) + WS.Unknown -> k' WS.Unknown + Yices -> liftIO $ WS.runYicesInOverride sym WS.defaultLogData clauses $ \case + WS.Sat ge -> k' (WS.Sat ge) + WS.Unsat x -> k' (WS.Unsat x) + WS.Unknown -> k' WS.Unknown + Z3 -> liftIO $ WS.runZ3InOverride sym WS.defaultLogData clauses $ \case + WS.Sat (ge, _) -> k' (WS.Sat ge) + WS.Unsat x -> k' (WS.Unsat x) + WS.Unknown -> k' WS.Unknown + + pure (CS.propertyName pr, satRes) -- Execute the action and return the results for each property runTransM spec proveProperties From 8d2a643035e6158ca1e6d450aee04659b108f578 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 12:19:26 -0500 Subject: [PATCH 158/483] copilot-theorem: Add Show and ShowF instances for counterexamples. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. The `CounterExample`, `SatResultCex`, and `CopilotValue` data types lack `Show` and `ShowF` instances, which makes it impractical for users to display them. This commit adds `Show` and `ShowF` instances for all three data types so that they can be shown. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 88 +++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 6e3fd53a9..eb692378f 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -69,6 +69,7 @@ import qualified Data.BitVector.Sized as BV import Data.Foldable (foldrM) import Data.List (genericLength) import qualified Data.Map as Map +import Data.Parameterized.Classes (ShowF) import Data.Parameterized.NatRepr import Data.Parameterized.Nonce import Data.Parameterized.Some @@ -100,6 +101,7 @@ data SatResult = Valid | Invalid | Unknown -- each property in a spec. This is largely the same as 'SatResult', except that -- 'InvalidCex' also records a 'CounterExample'. data SatResultCex = ValidCex | InvalidCex CounterExample | UnknownCex + deriving Show -- | Concrete values that cause a property in a Copilot specification to be -- invalid. As a simple example, consider the following spec: @@ -218,38 +220,42 @@ data SatResultCex = ValidCex | InvalidCex CounterExample | UnknownCex -- Be aware that counterexamples involving struct values are not currently -- supported. data CounterExample = CounterExample - { -- | A list of base cases in the proof, where each entry in the list - -- corresponds to a particular time step. For instance, the first element - -- in the list corresponds to the initial time step, the second element in - -- the list corresponds to the second time step, and so on. A 'False' entry - -- anywhere in this list will cause the overall proof to be 'InvalidCex'. - -- - -- Because the proof uses @k@-induction, the number of base cases (i.e., the - -- number of entries in this list) is equal to the value of @k@, which is - -- chosen using heuristics. - baseCases :: [Bool] - -- | Whether the induction step of the proof was valid or not. That is, - -- given an arbitrary time step @n@, if the property is assumed to hold at - -- time steps @n@, @n+1@, ..., @n+k@, then this will be @True@ is the - -- property can the be proven to hold at time step @n+k+1@ (and 'False' - -- otherwise). If this is 'False', then the overall proof will be - -- 'InvalidCex'. - , inductionStep :: Bool - -- | The concrete values in the Copilot specification's extern streams that - -- lead to the property being invalid. - -- - -- Each key in the 'Map.Map' is the 'CE.Name' of an extern stream paired - -- with a 'StreamOffset' representing the time step. The key's corresponding - -- value is the concrete value of the extern stream at that time step. - , concreteExternValues :: Map.Map (CE.Name, StreamOffset) (Some CopilotValue) - -- | The concrete values in the Copilot specification's streams (excluding - -- extern streams) that lead to the property being invalid. - -- - -- Each key in the 'Map.Map' is the 'CE.Id' of a stream paired with a - -- 'StreamOffset' representing the time step. The key's corresponding value - -- is the concrete value of the extern stream at that time step. - , concreteStreamValues :: Map.Map (CE.Id, StreamOffset) (Some CopilotValue) - } + { -- | A list of base cases in the proof, where each entry in the list + -- corresponds to a particular time step. For instance, the first element + -- in the list corresponds to the initial time step, the second element + -- in the list corresponds to the second time step, and so on. A 'False' + -- entry anywhere in this list will cause the overall proof to be + -- 'InvalidCex'. + -- + -- Because the proof uses @k@-induction, the number of base cases (i.e., + -- the number of entries in this list) is equal to the value of @k@, + -- which is chosen using heuristics. + baseCases :: [Bool] + -- | Whether the induction step of the proof was valid or not. That is, + -- given an arbitrary time step @n@, if the property is assumed to hold + -- at time steps @n@, @n+1@, ..., @n+k@, then this will be @True@ is the + -- property can the be proven to hold at time step @n+k+1@ (and 'False' + -- otherwise). If this is 'False', then the overall proof will be + -- 'InvalidCex'. + , inductionStep :: Bool + -- | The concrete values in the Copilot specification's extern streams + -- that lead to the property being invalid. + -- + -- Each key in the 'Map.Map' is the 'CE.Name' of an extern stream paired + -- with a 'StreamOffset' representing the time step. The key's + -- corresponding value is the concrete value of the extern stream at that + -- time step. + , concreteExternValues :: + Map.Map (CE.Name, StreamOffset) (Some CopilotValue) + -- | The concrete values in the Copilot specification's streams + -- (excluding extern streams) that lead to the property being invalid. + -- + -- Each key in the 'Map.Map' is the 'CE.Id' of a stream paired with a + -- 'StreamOffset' representing the time step. The key's corresponding + -- value is the concrete value of the extern stream at that time step. + , concreteStreamValues :: Map.Map (CE.Id, StreamOffset) (Some CopilotValue) + } + deriving Show -- | Attempt to prove all of the properties in a spec via an SMT solver (which -- must be installed locally on the host). Return an association list mapping @@ -624,6 +630,24 @@ expectedBool what xe = data CopilotValue a where CopilotValue :: CT.Typed a => CT.Type a -> a -> CopilotValue a +instance Show (CopilotValue a) where + showsPrec p (CopilotValue ty val) = + case ty of + CT.Bool -> showsPrec p val + CT.Int8 -> showsPrec p val + CT.Int16 -> showsPrec p val + CT.Int32 -> showsPrec p val + CT.Int64 -> showsPrec p val + CT.Word8 -> showsPrec p val + CT.Word16 -> showsPrec p val + CT.Word32 -> showsPrec p val + CT.Word64 -> showsPrec p val + CT.Float -> showsPrec p val + CT.Double -> showsPrec p val + CT.Array {} -> showsPrec p val + CT.Struct {} -> showsPrec p val +instance ShowF CopilotValue + -- | Convert a symbolic 'XExpr' into a concrete 'CopilotValue'. -- -- Struct values are not currently supported, so attempting to convert an From 0727256b0cfafbe458dc7df8d8134ff766cf3d00 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 24 Feb 2025 16:01:09 -0500 Subject: [PATCH 159/483] copilot-theorem: Add test cases for proveWithCounterExample. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. A prior commit has introduced a `proveWithCounterExample` function, which provides a counterexample when a property is proven invalid. This commit updates the test suite to ensure that basic uses of `proveWithCounterExample` work as intended. --- copilot-theorem/copilot-theorem.cabal | 1 + .../tests/Test/Copilot/Theorem/What4.hs | 123 ++++++++++++++++-- 2 files changed, 113 insertions(+), 11 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index e708c0a6c..c3d2496dc 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -121,6 +121,7 @@ test-suite unit-tests build-depends: base + , HUnit , QuickCheck , test-framework , test-framework-quickcheck2 diff --git a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs index dbd98eb4a..751310b5e 100644 --- a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs +++ b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs @@ -10,14 +10,15 @@ import Data.Int (Int8) import Data.Word (Word32) import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.HUnit (assertFailure) import Test.QuickCheck (Arbitrary (arbitrary), Property, arbitrary, forAll) import Test.QuickCheck.Monadic (monadicIO, run) -- External imports: Copilot -import Copilot.Core.Expr (Expr (Const, Op1, Op2)) +import Copilot.Core.Expr (Expr (Const, Drop, Op1, Op2), Id) import Copilot.Core.Operators (Op1 (..), Op2 (..)) -import Copilot.Core.Spec (Spec (..)) +import Copilot.Core.Spec (Spec (..), Stream (..)) import qualified Copilot.Core.Spec as Copilot import Copilot.Core.Type (Field (..), Struct (toValues, typeName), @@ -25,7 +26,9 @@ import Copilot.Core.Type (Field (..), Value (..)) -- Internal imports: Modules being tested -import Copilot.Theorem.What4 (SatResult (..), Solver (..), prove) +import Copilot.Theorem.What4 (CounterExample (..), SatResult (..), + SatResultCex (..), Solver (..), prove, + proveWithCounterExample) -- * Constants @@ -37,6 +40,8 @@ tests = , testProperty "Prove via Z3 that false is invalid" testProveZ3False , testProperty "Prove via Z3 that x == x is valid" testProveZ3EqConst , testProperty "Prove via Z3 that a struct update is valid" testProveZ3StructUpdate + , testProperty "Counterexample with invalid base case" testCounterExampleBaseCase + , testProperty "Counterexample with invalid induction step" testCounterExampleInductionStep ] -- * Individual tests @@ -53,7 +58,7 @@ testProveZ3True = propName = "prop" spec :: Spec - spec = propSpec propName $ Const typeOf True + spec = propSpec propName [] $ Const typeOf True -- | Test that Z3 is able to prove the following expression invalid: -- @ @@ -67,7 +72,7 @@ testProveZ3False = propName = "prop" spec :: Spec - spec = propSpec propName $ Const typeOf False + spec = propSpec propName [] $ Const typeOf False -- | Test that Z3 is able to prove the following expresion valid: -- @ @@ -81,7 +86,7 @@ testProveZ3EqConst = forAll arbitrary $ \x -> propName = "prop" spec :: Int8 -> Spec - spec x = propSpec propName $ + spec x = propSpec propName [] $ Op2 (Eq typeOf) (Const typeOf x) (Const typeOf x) -- | Test that Z3 is able to prove the following expresion valid: @@ -97,7 +102,7 @@ testProveZ3StructUpdate = forAll arbitrary $ \x -> propName = "prop" spec :: TestStruct -> Spec - spec s = propSpec propName $ + spec s = propSpec propName [] $ Op2 (Eq typeOf) (getField @@ -116,6 +121,70 @@ testProveZ3StructUpdate = forAll arbitrary $ \x -> add1 :: Expr Word32 -> Expr Word32 add1 x = Op2 (Add typeOf) x (Const typeOf 1) +-- | Test that Z3 is able to produce a counterexample to the following property, +-- where the base case is proved invalid: +-- +-- @ +-- let s :: Stream Bool +-- s = [False] ++ constant True +-- in forAll s +-- @ +testCounterExampleBaseCase :: Property +testCounterExampleBaseCase = + monadicIO $ run $ + checkCounterExample Z3 propName spec $ \cex -> + pure $ not $ and $ baseCases cex + where + propName :: String + propName = "prop" + + -- s = [False] ++ constant True + s :: Stream + s = Stream + { streamId = sId + , streamBuffer = [False] + , streamExpr = Const typeOf True + , streamExprType = typeOf + } + + sId :: Id + sId = 0 + + spec :: Spec + spec = propSpec propName [s] $ Drop typeOf 0 sId + +-- | Test that Z3 is able to produce a counterexample to the following property, +-- where the induction step is proved invalid: +-- +-- @ +-- let s :: Stream Bool +-- s = [True] ++ constant False +-- in forAll s +-- @ +testCounterExampleInductionStep :: Property +testCounterExampleInductionStep = + monadicIO $ run $ + checkCounterExample Z3 propName spec $ \cex -> + pure $ not $ inductionStep cex + where + propName :: String + propName = "prop" + + -- s = [True] ++ constant False + s :: Stream + s = Stream + { streamId = sId + , streamBuffer = [True] + , streamExpr = Const typeOf False + , streamExprType = typeOf + } + + sId :: Id + sId = 0 + + spec :: Spec + spec = propSpec propName [s] $ Drop typeOf 0 sId + -- | A simple data type with a 'Struct' instance and a 'Field'. This is only -- used as part of 'testProveZ3StructUpdate'. newtype TestStruct = TestStruct { testField :: Field "testField" Word32 } @@ -145,12 +214,44 @@ checkResult solver propName spec expectation = do -- does not exist in the results, in which case the lookup returns 'Nothing'. return $ propResult == Just expectation +-- | Check that the solver produces an invalid result for the given property and +-- that the resulting 'CounterExample' satifies the given predicate. +checkCounterExample :: Solver + -> String + -> Spec + -> (CounterExample -> IO Bool) + -> IO Bool +checkCounterExample solver propName spec cexPred = do + results <- proveWithCounterExample solver spec + + -- Find the satisfiability result for propName. If the property name does not + -- exist in the results, raise an assertion failure. + propResult <- + case lookup propName results of + Just propResult -> + pure propResult + Nothing -> + assertFailure $ + "Could not find property in results: " ++ propName + + -- Assert that the solver returned an invalid result and pass the + -- counterexample to the predicate. If the result is anything other than + -- invalid, raise an assertion failure. + case propResult of + InvalidCex cex -> + cexPred cex + ValidCex {} -> + assertFailure "Expected invalid result, but result was valid" + UnknownCex {} -> + assertFailure "Expected invalid result, but result was unknown" + -- * Auxiliary --- | Build a 'Spec' that contains one property with the given name and defined --- by the given boolean expression. -propSpec :: String -> Expr Bool -> Spec -propSpec propName propExpr = Spec [] [] [] [Copilot.Property propName propExpr] +-- | Build a 'Spec' that contains one property with the given name, which +-- contains the given streams, and is defined by the given boolean expression. +propSpec :: String -> [Stream] -> Expr Bool -> Spec +propSpec propName propStreams propExpr = + Spec propStreams [] [] [Copilot.Property propName propExpr] -- | Equality for 'SatResult'. -- From ed24a0e0026198ff8b6910f5eb36e08824ef4c8f Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 24 Feb 2025 17:25:46 -0500 Subject: [PATCH 160/483] copilot: Add example of how to use proveWithCounterExample. Refs #589. Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. To demonstrate how to effectively use the newly added `proveWithCounterExample` function, this commit adds a new `examples/what4/ArithmeticCounterExamples.hs` function that behaves like `examples/what4/Arithmetic.hs`, but using `proveWithCounterExamples` instead of `prove`. --- copilot/copilot.cabal | 13 +++ .../what4/ArithmeticCounterExamples.hs | 110 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 copilot/examples/what4/ArithmeticCounterExamples.hs diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 2c1082075..272a3edb8 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -86,6 +86,19 @@ executable what4-arithmetic else buildable: False +executable what4-arithmetic-counterexamples + main-is: ArithmeticCounterExamples.hs + hs-source-dirs: examples/what4 + build-depends: base + , containers + , copilot + , copilot-theorem + default-language: Haskell2010 + if flag(examples) + buildable: True + else + buildable: False + executable what4-structs main-is: Structs.hs hs-source-dirs: examples/what4 diff --git a/copilot/examples/what4/ArithmeticCounterExamples.hs b/copilot/examples/what4/ArithmeticCounterExamples.hs new file mode 100644 index 000000000..d56f2b7b4 --- /dev/null +++ b/copilot/examples/what4/ArithmeticCounterExamples.hs @@ -0,0 +1,110 @@ +-- | An example showing the usage of the What4 backend in copilot-theorem for +-- simple arithmetic. This example uses the 'proveWithCounterExamples' function +-- to demonstrate counterexamples in the event of invalid properties. + +module Main where + +import qualified Prelude as P +import Control.Monad (void, forM_) +import qualified Data.Map as Map + +import Language.Copilot +import Copilot.Theorem.What4 + +spec :: Spec +spec = do + -- Define some external streams. Their values are not important, so external + -- streams suffice. + let eint8 :: Stream Int8 + eint8 = extern "eint8" Nothing + eword8 :: Stream Word8 + eword8 = extern "eword8" Nothing + efloat :: Stream Float + efloat = extern "efloat" Nothing + + -- The simplest example involving numbers: equality on constant values. + void $ prop "Example 1" (forAll ((constant (1 :: Int8)) == (constant 1))) + + -- Testing "a < a + 1". This should fail, because it isn't true. + void $ prop "Example 2" (forAll (eint8 < (eint8 + 1))) + + -- Adding another condition to the above property to make it true. + void $ prop "Example 3" (forAll ((eint8 < (eint8 + 1)) || (eint8 == 127))) + + -- Just like the previous example, but with words. + void $ prop "Example 4" (forAll ((eword8 < (eword8 + 1)) || (eword8 == 255))) + + -- An example with floats. + void $ prop "Example 5" (forAll ((2 * efloat) == (efloat + efloat))) + + -- Another example with floats. This fails, because it isn't true. + void $ prop "Example 6" (forAll ((efloat + 1) /= efloat)) + +main :: IO () +main = do + spec' <- reify spec + + -- Use Z3 to prove the properties. + results <- proveWithCounterExample Z3 spec' + + -- Print the results. + forM_ results $ \(nm, res) -> do + putStr $ nm <> ": " + case res of + ValidCex -> putStrLn "valid" + InvalidCex cex -> do + putStrLn "invalid" + putStrLn $ ppCounterExample cex + UnknownCex -> putStrLn "unknown" + +-- | Pretty-print a counterexample for user display. +ppCounterExample :: CounterExample -> String +ppCounterExample cex + | any P.not (baseCases cex) + = if Map.null baseCaseVals + then + " All possible extern values during the base case(s) " P.++ + "constitute a counterexample." + else + unlines $ + " The base cases failed with the following extern values:" : + map + (\((name, _), val) -> " " P.++ name P.++ ": " P.++ show val) + (Map.toList baseCaseVals) + + | P.not (inductionStep cex) + = if Map.null inductionStepVals + then + " All possible extern values during the induction step " P.++ + "constitute a counterexample." + else + unlines $ + " The induction step failed with the following extern values:" : + map + (\((name, _), val) -> " " P.++ name P.++ ": " P.++ show val) + (Map.toList inductionStepVals) + + | otherwise + = error $ + "ppCounterExample: " P.++ + "Counterexample without failing base cases or induction step" + where + allExternVals = concreteExternValues cex + + baseCaseVals = + Map.filterWithKey + (\(_, offset) _ -> + case offset of + AbsoluteOffset {} -> True + RelativeOffset {} -> False + ) + allExternVals + + inductionStepVals = + Map.filterWithKey + (\(_, offset) _ -> + case offset of + AbsoluteOffset {} -> False + RelativeOffset {} -> True + ) + allExternVals From a3d2310d5751692062678d44247046646b85fe7e Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 12:25:49 -0500 Subject: [PATCH 161/483] copilot-core: Document changes in CHANGELOG. Refs #589. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index e51b49366..69de85d76 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,5 +1,6 @@ -2025-01-28 +2025-02-24 * Fix typo in documentation. (#587) + * Add a Show instance for Type. (#589) 2025-01-07 * Version bump (4.2). (#577) From 134bbdc201dac8934aa63601ceba9f81d7cf4afa Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 10 Feb 2025 12:27:49 -0500 Subject: [PATCH 162/483] copilot-theorem: Document changes in CHANGELOG. Refs #589. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 4251f8875..f503de4e0 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,6 +1,7 @@ -2025-01-28 +2025-02-24 * Fix multiple typos in README. (#560) * Fix typo in documentation. (#587) + * Add function to produce counterexamples for invalid properties. (#589) 2025-01-07 * Version bump (4.2). (#577) From 9861071718850fb8ef85e533a6c0d6d083f69094 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 24 Feb 2025 17:29:55 -0500 Subject: [PATCH 163/483] copilot: Document changes in CHANGELOG. Refs #589. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index d6574cd3e..64c5f7535 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,6 +1,7 @@ -2025-01-31 +2025-02-24 * Include missing dependencies in installation instructions. (#591) * Update version of GHC in README. (#590) + * Add example of how to use proveWithCounterExample. (#589) 2025-01-07 * Version bump (4.2). (#577) From 6061af73cc83d4acc1cf3680c602ec44f60515e9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 5 Mar 2025 09:17:15 +0000 Subject: [PATCH 164/483] copilot: List all Copilot packages in installation command in README. Refs #597. The current installation instructions do not expose all packages, meaning that users can only access whatever is re-exported by the package copilot. This prevents users from accessing many key features of Copilot, and from compiling many specifications. This commit updates the manual installation instructions to include all packages, thus exposing all Copilot modules and allowing specifications that users write to compile. --- copilot/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/copilot/README.md b/copilot/README.md index 7ce1e482e..efdc58d28 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -112,7 +112,8 @@ Once the compiler is installed, install Copilot from [Hackage](https://hackage.haskell.org/package/copilot) with: ```sh -cabal v2-install --lib copilot +cabal v2-install --lib copilot copilot-core copilot-c99 copilot-language \ + copilot-theorem copilot-libraries copilot-interpreter copilot-prettyprinter ``` To test that Copilot is available, execute the following: @@ -141,7 +142,8 @@ Once the compiler is installed, install Copilot from [Hackage](https://hackage.haskell.org/package/copilot) with: ```sh -$ cabal v2-install --lib copilot +$ cabal v2-install --lib copilot copilot-core copilot-c99 copilot-language \ + copilot-theorem copilot-libraries copilot-interpreter copilot-prettyprinter ``` To test that Copilot is available, execute the following: From ea702c949b7f8fcf9881e37e5b25406d3f95b7d1 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 5 Mar 2025 09:19:25 +0000 Subject: [PATCH 165/483] copilot: Document changes in CHANGELOG. Refs #597. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 64c5f7535..4961ed466 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,7 +1,8 @@ -2025-02-24 +2025-03-05 * Include missing dependencies in installation instructions. (#591) * Update version of GHC in README. (#590) * Add example of how to use proveWithCounterExample. (#589) + * List all Copilot packages in installation command in README. (#597) 2025-01-07 * Version bump (4.2). (#577) From 5b08e817fb6d23485bc86553a820135896fc54b1 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 5 Mar 2025 16:59:35 -0500 Subject: [PATCH 166/483] copilot-core: Preserve proposition quantifiers. Refs Copilot-Language#254. Currently, `copilot-language` remembers whether a proposition (i.e., a stream of booleans) is quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier is discarded. This means that a `copilot-core` `Property` does not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. This commit changes the `copilot-core` API to preserve quantifier information. Specifically, it introduces a `Prop` data type in `copilot-core` (largely inspired by a data type of the same name in `copilot-language`) to record a proposition's quantifier, and it changes the `propertyExpr :: Expr Bool` field of `copilot-core`'s `Property` data type to `propertyProp :: Prop`. This commit also introduces an `extractProp :: Prop -> Expr Bool` function for retrieving the underlying boolean expression. Generally, this function should not be used, as different quantifiers usually require different treatment, and misuse of the `extractProp` function can potentially lead to unsoundness. There are a handful of places where the use of `extractProp` is justified, however. In each such place, a comment should be left to justify why the use of `extractProp` is sound. --- copilot-core/src/Copilot/Core/Spec.hs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/copilot-core/src/Copilot/Core/Spec.hs b/copilot-core/src/Copilot/Core/Spec.hs index 7a71c780e..6614d0621 100644 --- a/copilot-core/src/Copilot/Core/Spec.hs +++ b/copilot-core/src/Copilot/Core/Spec.hs @@ -20,6 +20,8 @@ module Copilot.Core.Spec , Trigger (..) , Spec (..) , Property (..) + , Prop (..) + , extractProp ) where @@ -62,9 +64,26 @@ data Trigger = Trigger -- universally quantified over time. data Property = Property { propertyName :: Name - , propertyExpr :: Expr Bool + , propertyProp :: Prop } +-- | A proposition, representing a boolean stream that is existentially or +-- universally quantified over time. +data Prop + = Forall (Expr Bool) + | Exists (Expr Bool) + +-- | Extract the underlying stream from a quantified proposition. +-- +-- Think carefully before using this function, as this function will remove the +-- quantifier from a proposition. Universally quantified streams usually require +-- separate treatment from existentially quantified ones, so carelessly using +-- this function to remove quantifiers can result in hard-to-spot soundness +-- bugs. +extractProp :: Prop -> Expr Bool +extractProp (Forall e) = e +extractProp (Exists e) = e + -- | A Copilot specification is a list of streams, together with monitors on -- these streams implemented as observers, triggers or properties. data Spec = Spec From 0c3e7021d395563aa9ce10d7daab0dca5505689e Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 5 Mar 2025 17:03:18 -0500 Subject: [PATCH 167/483] copilot-language: Preserve proposition quantifiers. Refs Copilot-Language#254. Currently, `copilot-language` remembers whether a proposition (i.e., a stream of booleans) is quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier is discarded. This means that a `copilot-core` `Property` does not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. Now that `copilot-core` preserves quantifier information in its API, this commit updates `copilot-language` to respect quantifiers when translating into `copilot-core`. --- .../src/Copilot/Language/Analyze.hs | 13 +++++++++++-- copilot-language/src/Copilot/Language/Reify.hs | 18 +++++++++++++++--- copilot-language/src/Copilot/Language/Spec.hs | 12 +++++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/copilot-language/src/Copilot/Language/Analyze.hs b/copilot-language/src/Copilot/Language/Analyze.hs index 04de14ed4..e7065f7b4 100644 --- a/copilot-language/src/Copilot/Language/Analyze.hs +++ b/copilot-language/src/Copilot/Language/Analyze.hs @@ -110,7 +110,11 @@ analyzeObserver refStreams (Observer _ e) = analyzeExpr refStreams e -- -- This function can fail with one of the exceptions in 'AnalyzeException'. analyzeProperty :: IORef Env -> Property -> IO () -analyzeProperty refStreams (Property _ e) = analyzeExpr refStreams e +analyzeProperty refStreams (Property _ p) = + -- Soundness note: it is OK to call `extractProp` here to drop the quantifier + -- from the proposition `p`, as the analysis does not depend on what the + -- quantifier is. + analyzeExpr refStreams (extractProp p) data SeenExtern = NoExtern | SeenFun @@ -291,7 +295,12 @@ specExts refStreams spec = do env' args propertyExts :: ExternEnv -> Property -> IO ExternEnv - propertyExts env (Property _ stream) = collectExts refStreams stream env + propertyExts env (Property _ p) = + -- Soundness note: it is OK to call `extractProp` here to drop the + -- quantifier from the proposition `p`. This is because we are simply + -- gathering externs from `p`, and the presence of externs does not depend + -- on what the quantifier is. + collectExts refStreams (extractProp p) env theoremExts :: ExternEnv -> (Property, UProof) -> IO ExternEnv theoremExts env (p, _) = propertyExts env p diff --git a/copilot-language/src/Copilot/Language/Reify.hs b/copilot-language/src/Copilot/Language/Reify.hs index 092458b78..60bae450b 100644 --- a/copilot-language/src/Copilot/Language/Reify.hs +++ b/copilot-language/src/Copilot/Language/Reify.hs @@ -4,6 +4,7 @@ -- specification. {-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE Safe #-} @@ -105,11 +106,22 @@ mkProperty -> IORef [Core.Stream] -> Property -> IO Core.Property -mkProperty refMkId refStreams refMap (Property name guard) = do - w1 <- mkExpr refMkId refStreams refMap guard +mkProperty refMkId refStreams refMap (Property name p) = do + p' <- mkProp refMkId refStreams refMap p return Core.Property { Core.propertyName = name - , Core.propertyExpr = w1 } + , Core.propertyProp = p' } + +-- | Transform a Copilot proposition into a Copilot Core proposition. +mkProp :: IORef Int + -> IORef (Map Core.Id) + -> IORef [Core.Stream] + -> Prop a + -> IO Core.Prop +mkProp refMkId refStreams refMap prop = + case prop of + Forall e -> Core.Forall <$> mkExpr refMkId refStreams refMap e + Exists e -> Core.Exists <$> mkExpr refMkId refStreams refMap e -- | Transform a Copilot stream expression into a Copilot Core expression. {-# INLINE mkExpr #-} diff --git a/copilot-language/src/Copilot/Language/Spec.hs b/copilot-language/src/Copilot/Language/Spec.hs index b5fc94b61..0127cd24b 100644 --- a/copilot-language/src/Copilot/Language/Spec.hs +++ b/copilot-language/src/Copilot/Language/Spec.hs @@ -153,7 +153,7 @@ trigger name e args = tell [TriggerItem $ Trigger name e args] -- | A property, representing a boolean stream that is existentially or -- universally quantified over time. data Property where - Property :: String -> Stream Bool -> Property + Property :: String -> Prop a -> Property -- | A proposition, representing the quantification of a boolean streams over -- time. @@ -170,6 +170,12 @@ exists :: Stream Bool -> Prop Existential exists = Exists -- | Extract the underlying stream from a quantified proposition. +-- +-- Think carefully before using this function, as this function will remove the +-- quantifier from a proposition. Universally quantified streams usually require +-- separate treatment from existentially quantified ones, so carelessly using +-- this function to remove quantifiers can result in hard-to-spot soundness +-- bugs. extractProp :: Prop a -> Stream Bool extractProp (Forall p) = p extractProp (Exists p) = p @@ -180,7 +186,7 @@ extractProp (Exists p) = p -- This function returns, in the monadic context, a reference to the -- proposition. prop :: String -> Prop a -> Writer [SpecItem] (PropRef a) -prop name e = tell [PropertyItem $ Property name (extractProp e)] +prop name e = tell [PropertyItem $ Property name e] >> return (PropRef name) -- | A theorem, or proposition together with a proof. @@ -188,7 +194,7 @@ prop name e = tell [PropertyItem $ Property name (extractProp e)] -- This function returns, in the monadic context, a reference to the -- proposition. theorem :: String -> Prop a -> Proof a -> Writer [SpecItem] (PropRef a) -theorem name e (Proof p) = tell [TheoremItem (Property name (extractProp e), p)] +theorem name e (Proof p) = tell [TheoremItem (Property name e, p)] >> return (PropRef name) -- | Construct a function argument from a stream. From 8e46a139b96da91ffd4e078af3c1526e6230467c Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 5 Mar 2025 17:06:14 -0500 Subject: [PATCH 168/483] copilot-prettyprinter: Preserve proposition quantifiers. Refs Copilot-Language#254. Currently, `copilot-language` remembers whether a proposition (i.e., a stream of booleans) is quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier is discarded. This means that a `copilot-core` `Property` does not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. Now that `copilot-core` preserves quantifier information in its API, this commit updates `copilot-prettyprinter` to pretty-print quantifier information appropriately. --- copilot-prettyprinter/src/Copilot/PrettyPrint.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs index 50be4466f..4bbad626b 100644 --- a/copilot-prettyprinter/src/Copilot/PrettyPrint.hs +++ b/copilot-prettyprinter/src/Copilot/PrettyPrint.hs @@ -175,10 +175,15 @@ ppProperty :: Property -> Doc ppProperty Property { propertyName = name - , propertyExpr = e } + , propertyProp = p } = text "property \"" <> text name <> text "\"" <+> text "=" - <+> ppExpr e + <+> ppProp p + +-- | Pretty-print a Copilot proposition. +ppProp :: Prop -> Doc +ppProp (Forall e) = text "forall" <+> parens (ppExpr e) +ppProp (Exists e) = text "exists" <+> parens (ppExpr e) -- | Pretty-print a Copilot specification, in the following order: -- From bce4c104862757b229dc931b1a35e477b64d3a39 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 5 Mar 2025 17:07:58 -0500 Subject: [PATCH 169/483] copilot-theorem: Preserve proposition quantifiers. Refs Copilot-Language#254. Currently, `copilot-language` remembers whether a proposition (i.e., a stream of booleans) is quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier is discarded. This means that a `copilot-core` `Property` does not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. Now that `copilot-core` preserves quantifier information in its API, this commit updates `copilot-theorem` to make use of quantifier information when proving theorems. --- copilot-theorem/src/Copilot/Theorem/IL/Translate.hs | 9 +++++++-- .../src/Copilot/Theorem/TransSys/Translate.hs | 2 +- copilot-theorem/src/Copilot/Theorem/What4.hs | 9 +++++---- copilot-theorem/tests/Test/Copilot/Theorem/What4.hs | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/IL/Translate.hs b/copilot-theorem/src/Copilot/Theorem/IL/Translate.hs index 0bb6bfa37..c1a6606ba 100644 --- a/copilot-theorem/src/Copilot/Theorem/IL/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/IL/Translate.hs @@ -63,8 +63,13 @@ translate' b (C.Spec {C.specStreams, C.specProperties}) = runTrans b $ do localConstraints <- popLocalConstraints properties <- Map.fromList <$> forM specProperties - (\(C.Property {C.propertyName, C.propertyExpr}) -> do - e' <- expr propertyExpr + (\(C.Property {C.propertyName, C.propertyProp}) -> do + -- Soundness note: it is OK to call `extractProp` here to drop the + -- quantifier from the proposition `propertyProp`. This is because we + -- IL translation always occurs within the context of a function that + -- returns a `Proof`, and these `Proof` functions are always careful to + -- use `Prover`s that respect the propositions's quantifier. + e' <- expr (C.extractProp propertyProp) propConds <- popLocalConstraints return (propertyName, (propConds, e'))) diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs index cc557e429..68ddb8806 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs @@ -139,7 +139,7 @@ streamOfProp :: C.Property -> C.Stream streamOfProp prop = C.Stream { C.streamId = 42 , C.streamBuffer = [] - , C.streamExpr = C.propertyExpr prop + , C.streamExpr = C.extractProp (C.propertyProp prop) , C.streamExprType = C.Bool } stream :: C.Stream -> Trans Node diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index eb692378f..3cf43861d 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -350,9 +350,10 @@ proveInternal solver spec k = do -- This process performs k-induction where we use @k = maxBufLen@. -- The choice for @k@ is heuristic, but often effective. let proveProperties = forM (CS.specProperties spec) $ \pr -> do + let prop = CS.extractProp (CS.propertyProp pr) -- State the base cases for k induction. base_cases <- forM [0 .. maxBufLen - 1] $ \i -> do - xe <- translateExpr sym mempty (CS.propertyExpr pr) (AbsoluteOffset i) + xe <- translateExpr sym mempty prop (AbsoluteOffset i) case xe of XBool p -> return p _ -> expectedBool "Property" xe @@ -360,7 +361,7 @@ proveInternal solver spec k = do -- Translate the induction hypothesis for all values up to maxBufLen in -- the past ind_hyps <- forM [0 .. maxBufLen-1] $ \i -> do - xe <- translateExpr sym mempty (CS.propertyExpr pr) (RelativeOffset i) + xe <- translateExpr sym mempty prop (RelativeOffset i) case xe of XBool hyp -> return hyp _ -> expectedBool "Property" xe @@ -369,7 +370,7 @@ proveInternal solver spec k = do ind_goal <- do xe <- translateExpr sym mempty - (CS.propertyExpr pr) + prop (RelativeOffset maxBufLen) case xe of XBool p -> return p @@ -599,7 +600,7 @@ computeAssumptions sym properties spec = -- user-provided property assumptions. specPropertyExprs :: [CE.Expr Bool] specPropertyExprs = - [ CS.propertyExpr p + [ CS.extractProp (CS.propertyProp p) | p <- CS.specProperties spec , elem (CS.propertyName p) properties ] diff --git a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs index 751310b5e..03e6b8dad 100644 --- a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs +++ b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs @@ -251,7 +251,7 @@ checkCounterExample solver propName spec cexPred = do -- contains the given streams, and is defined by the given boolean expression. propSpec :: String -> [Stream] -> Expr Bool -> Spec propSpec propName propStreams propExpr = - Spec propStreams [] [] [Copilot.Property propName propExpr] + Spec propStreams [] [] [Copilot.Property propName (Copilot.Forall propExpr)] -- | Equality for 'SatResult'. -- From d1392428abc45c5b42017cf494f31b50da2abf66 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 4 Feb 2025 08:03:05 -0500 Subject: [PATCH 170/483] copilot-theorem: Reject existentially quantified propositions in What4 backend. Refs #254. The functions in `Copilot.Theorem.What4` (e.g., `prove`) take a proposition and attempt to prove that it is valid at all possible time steps. Currently, these functions do this regardless of whether the proposition is universally quantified or existentially quantified, which is unsound. The reason that this happens is because the functions call `extractProp` on the proposition being proven, causing the proposition's quantifier not to be taken into account. This commit removes the unsound uses of `extractProp` and instead checks if the proposition was actually quantified universally (i.e., with a `Forall`), which is the intended use case for `Copilot.Theorem.What4`. If the proposition was instead quantified existentially (i.e., with an `Exists`), then a `ProveException` will be thrown. Warnings have been added to Haddocks of the relevant functions that can potentially throw a `ProveException`. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 41 +++++++++- .../tests/Test/Copilot/Theorem/What4.hs | 79 +++++++++++++++---- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 3cf43861d..818ac9b3a 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -32,6 +32,12 @@ -- We perform @k@-induction on all the properties in a given specification where -- @k@ is chosen to be the maximum amount of delay on any of the involved -- streams. This is a heuristic choice, but often effective. +-- +-- The functions in this module are only designed to prove universally +-- quantified propositions (i.e., propositions that use @forAll@). Attempting to +-- prove an existentially quantified proposition (i.e., propositions that use +-- @exists@) will cause a 'UnexpectedExistentialProposition' exception to be +-- thrown. module Copilot.Theorem.What4 ( -- * Proving properties about Copilot specifications prove @@ -40,6 +46,7 @@ module Copilot.Theorem.What4 , proveWithCounterExample , SatResultCex(..) , CounterExample(..) + , ProveException(..) -- * Bisimulation proofs about @copilot-c99@ code , computeBisimulationProofBundle , BisimulationProofBundle(..) @@ -63,6 +70,7 @@ import qualified What4.InterpretedFloatingPoint as WFP import qualified What4.Solver as WS import qualified What4.Solver.DReal as WS +import Control.Exception (Exception, throw) import Control.Monad (forM) import Control.Monad.State import qualified Data.BitVector.Sized as BV @@ -257,9 +265,24 @@ data CounterExample = CounterExample } deriving Show +-- | Exceptions that can arise when attempting a proof. +data ProveException + = UnexpectedExistentialProposition + -- ^ The functions in "Copilot.Theorem.What4" can only prove properties with + -- universally quantified propositions. The functions in + -- "Copilot.Theorem.What4" will throw this exception if they encounter an + -- existentially quantified proposition. + deriving Show + +instance Exception ProveException + -- | Attempt to prove all of the properties in a spec via an SMT solver (which -- must be installed locally on the host). Return an association list mapping -- the names of each property to the result returned by the solver. +-- +-- PRE: All of the properties in the 'CS.Spec' use universally quantified +-- propositions. Attempting to supply an existentially quantified proposition +-- will cause a 'UnexpectedExistentialProposition' exception to be thrown. prove :: Solver -- ^ Solver to use -> CS.Spec @@ -350,7 +373,12 @@ proveInternal solver spec k = do -- This process performs k-induction where we use @k = maxBufLen@. -- The choice for @k@ is heuristic, but often effective. let proveProperties = forM (CS.specProperties spec) $ \pr -> do - let prop = CS.extractProp (CS.propertyProp pr) + -- This function only supports universally quantified propositions, so + -- throw an exception if we encounter an existentially quantified + -- proposition. + let prop = case CS.propertyProp pr of + CS.Forall p -> p + CS.Exists {} -> throw UnexpectedExistentialProposition -- State the base cases for k induction. base_cases <- forM [0 .. maxBufLen - 1] $ \i -> do xe <- translateExpr sym mempty prop (AbsoluteOffset i) @@ -420,6 +448,10 @@ proveInternal solver spec k = do -- to carry out a bisimulation proof that establishes a correspondence between -- the states of the Copilot stream program and the C code that @copilot-c99@ -- would generate for that Copilot program. +-- +-- PRE: All of the properties in the 'CS.Spec' use universally quantified +-- propositions. Attempting to supply an existentially quantified proposition +-- will cause a 'UnexpectedExistentialProposition' exception to be thrown. computeBisimulationProofBundle :: WFP.IsInterpretedFloatSymExprBuilder sym => sym @@ -581,6 +613,10 @@ computeExternalInputs sym = do return (nm, Some tp, v) -- | Compute the user-provided property assumptions in a Copilot program. +-- +-- PRE: All of the properties in the 'CS.Spec' use universally quantified +-- propositions. Attempting to supply an existentially quantified proposition +-- will cause a 'UnexpectedExistentialProposition' exception to be thrown. computeAssumptions :: forall sym. WFP.IsInterpretedFloatSymExprBuilder sym @@ -603,6 +639,9 @@ computeAssumptions sym properties spec = [ CS.extractProp (CS.propertyProp p) | p <- CS.specProperties spec , elem (CS.propertyName p) properties + , let prop = case CS.propertyProp p of + CS.Forall pr -> pr + CS.Exists {} -> throw UnexpectedExistentialProposition ] -- Compute all of the what4 predicates corresponding to each user-provided diff --git a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs index 03e6b8dad..edd137700 100644 --- a/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs +++ b/copilot-theorem/tests/Test/Copilot/Theorem/What4.hs @@ -1,4 +1,6 @@ -{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} -- The following warning is disabled due to a necessary instance of SatResult -- defined in this module. {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -6,11 +8,15 @@ module Test.Copilot.Theorem.What4 where -- External imports +import Control.Exception (Exception, try) import Data.Int (Int8) +import Data.Proxy (Proxy (..)) +import Data.Typeable (typeRep) import Data.Word (Word32) import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) -import Test.HUnit (assertFailure) +import Test.HUnit (Assertion, assertBool, + assertFailure) import Test.QuickCheck (Arbitrary (arbitrary), Property, arbitrary, forAll) import Test.QuickCheck.Monadic (monadicIO, run) @@ -26,9 +32,9 @@ import Copilot.Core.Type (Field (..), Value (..)) -- Internal imports: Modules being tested -import Copilot.Theorem.What4 (CounterExample (..), SatResult (..), - SatResultCex (..), Solver (..), prove, - proveWithCounterExample) +import Copilot.Theorem.What4 (CounterExample (..), ProveException (..), + SatResult (..), SatResultCex (..), Solver (..), + prove, proveWithCounterExample) -- * Constants @@ -42,6 +48,7 @@ tests = , testProperty "Prove via Z3 that a struct update is valid" testProveZ3StructUpdate , testProperty "Counterexample with invalid base case" testCounterExampleBaseCase , testProperty "Counterexample with invalid induction step" testCounterExampleInductionStep + , testProperty "Check that the What4 backend rejects existential quantification" testWhat4ExistsException ] -- * Individual tests @@ -58,7 +65,7 @@ testProveZ3True = propName = "prop" spec :: Spec - spec = propSpec propName [] $ Const typeOf True + spec = forallPropSpec propName [] $ Const typeOf True -- | Test that Z3 is able to prove the following expression invalid: -- @ @@ -72,7 +79,7 @@ testProveZ3False = propName = "prop" spec :: Spec - spec = propSpec propName [] $ Const typeOf False + spec = forallPropSpec propName [] $ Const typeOf False -- | Test that Z3 is able to prove the following expresion valid: -- @ @@ -86,7 +93,7 @@ testProveZ3EqConst = forAll arbitrary $ \x -> propName = "prop" spec :: Int8 -> Spec - spec x = propSpec propName [] $ + spec x = forallPropSpec propName [] $ Op2 (Eq typeOf) (Const typeOf x) (Const typeOf x) -- | Test that Z3 is able to prove the following expresion valid: @@ -102,7 +109,7 @@ testProveZ3StructUpdate = forAll arbitrary $ \x -> propName = "prop" spec :: TestStruct -> Spec - spec s = propSpec propName [] $ + spec s = forallPropSpec propName [] $ Op2 (Eq typeOf) (getField @@ -151,7 +158,7 @@ testCounterExampleBaseCase = sId = 0 spec :: Spec - spec = propSpec propName [s] $ Drop typeOf 0 sId + spec = forallPropSpec propName [s] $ Drop typeOf 0 sId -- | Test that Z3 is able to produce a counterexample to the following property, -- where the induction step is proved invalid: @@ -183,7 +190,23 @@ testCounterExampleInductionStep = sId = 0 spec :: Spec - spec = propSpec propName [s] $ Drop typeOf 0 sId + spec = forallPropSpec propName [s] $ Drop typeOf 0 sId + +-- | Test that @copilot-theorem@'s @what4@ backend will throw an exception if it +-- attempts to prove an existentially quantified proposition. +testWhat4ExistsException :: Property +testWhat4ExistsException = + monadicIO $ run $ + checkException (prove Z3 spec) isUnexpectedExistentialProposition + where + isUnexpectedExistentialProposition :: ProveException -> Bool + isUnexpectedExistentialProposition UnexpectedExistentialProposition = True + + propName :: String + propName = "prop" + + spec :: Spec + spec = existsPropSpec propName [] $ Const typeOf True -- | A simple data type with a 'Struct' instance and a 'Field'. This is only -- used as part of 'testProveZ3StructUpdate'. @@ -245,14 +268,42 @@ checkCounterExample solver propName spec cexPred = do UnknownCex {} -> assertFailure "Expected invalid result, but result was unknown" +-- | Check that the given 'IO' action throws a particular exception. This is +-- largely taken from the implementation of @shouldThrow@ in +-- @hspec-expectations@ (note that this test suite uses @test-framework@ instead +-- of @hspec@). +checkException :: forall e a. Exception e => IO a -> (e -> Bool) -> Assertion +checkException action p = do + r <- try action + case r of + Right _ -> + assertFailure $ + "did not get expected exception: " ++ exceptionType + Left e -> + assertBool + ("predicate failed on expected exception: " ++ exceptionType ++ + "\n" ++ show e) + (p e) + where + -- String representation of the expected exception's type + exceptionType = show $ typeRep $ Proxy @e + -- * Auxiliary -- | Build a 'Spec' that contains one property with the given name, which --- contains the given streams, and is defined by the given boolean expression. -propSpec :: String -> [Stream] -> Expr Bool -> Spec -propSpec propName propStreams propExpr = +-- contains the given streams, and is defined by the given boolean expression, +-- which is universally quantified. +forallPropSpec :: String -> [Stream] -> Expr Bool -> Spec +forallPropSpec propName propStreams propExpr = Spec propStreams [] [] [Copilot.Property propName (Copilot.Forall propExpr)] +-- | Build a 'Spec' that contains one property with the given name, which +-- contains the given streams, and is defined by the given boolean expression, +-- which is existentially quantified. +existsPropSpec :: String -> [Stream] -> Expr Bool -> Spec +existsPropSpec propName propStreams propExpr = + Spec propStreams [] [] [Copilot.Property propName (Copilot.Exists propExpr)] + -- | Equality for 'SatResult'. -- -- This is an orphan instance, so we suppress the warning that GHC would From 3ea5f1206c608528fbfca44e527d888eba7036e6 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 4 Feb 2025 13:33:55 -0500 Subject: [PATCH 171/483] copilot-core: Document changes in CHANGELOG. Refs #254. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 69de85d76..dc86d9739 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,6 +1,7 @@ -2025-02-24 +2025-02-28 * Fix typo in documentation. (#587) * Add a Show instance for Type. (#589) + * Add a Prop type to capture how a property is quantified. (#254) 2025-01-07 * Version bump (4.2). (#577) From b4664cd1d1b2a90ff2ed3ff5a91793aaa048f096 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 4 Feb 2025 13:36:14 -0500 Subject: [PATCH 172/483] copilot-language: Document changes in CHANGELOG. Refs #254. --- copilot-language/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index f4668a9e1..2aa4f6008 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,5 +1,6 @@ -2025-01-28 +2025-02-28 * Fix typo in documentation. (#587) + * Record how a Property's underlying proposition is quantified. (#254) 2025-01-07 * Version bump (4.2). (#577) From 88a67f5eb876b25cbc440e5810247d5df7f4352c Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 4 Feb 2025 13:37:42 -0500 Subject: [PATCH 173/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #254. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index b51261ae8..f930b53d0 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2025-02-28 + * Update pretty-printing to handle Props. (#254) + 2025-01-07 * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprExpr. (#565) From 700508abfcefea5c59517e0bdc3087735fdaa692 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 4 Feb 2025 13:42:29 -0500 Subject: [PATCH 174/483] copilot-theorem: Document changes in CHANGELOG. Refs #254. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index f503de4e0..1c76ef96a 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,7 +1,8 @@ -2025-02-24 +2025-02-28 * Fix multiple typos in README. (#560) * Fix typo in documentation. (#587) * Add function to produce counterexamples for invalid properties. (#589) + * Reject existentially quantified properties in What4 backend. (#254) 2025-01-07 * Version bump (4.2). (#577) From 83086bf4fbc940dff9e0c742068414dff01d92c6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 03:28:52 -0800 Subject: [PATCH 175/483] copilot-language: Remove deprecated function Copilot.Language.Operators.Array.(.!!). Refs #599. The function `copilot-language:Copilot.Language.Operators.Array.(.!!)` was deprecated due to the introduction of a new, common interface to interact with structures and arrays. The original function was deprecated in Copilot 4 and no messages have been received requesting that it be kept in this library. Per our internal policy of waiting 3 versions from deprecation until a public interface declaration can be removed, this function can now be removed. This commit removes the function `copilot-language:Copilot.Language.Operators.Array.(.!!)`. --- .../src/Copilot/Language/Operators/Array.hs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/copilot-language/src/Copilot/Language/Operators/Array.hs b/copilot-language/src/Copilot/Language/Operators/Array.hs index dd5840439..17bb3bff3 100644 --- a/copilot-language/src/Copilot/Language/Operators/Array.hs +++ b/copilot-language/src/Copilot/Language/Operators/Array.hs @@ -8,8 +8,7 @@ -- | Combinators to deal with streams carrying arrays. module Copilot.Language.Operators.Array - ( (.!!) - , (!) + ( (!) , (!!) , (=:) , (=$) @@ -34,18 +33,6 @@ import Prelude hiding ((!!)) => Stream (Array n t) -> Stream Word32 -> Stream t arr ! n = Op2 (Index typeOf) arr n --- | Create a stream that carries an element of an array in another stream. --- --- This function implements a projection of the element of an array at a given --- position, over time. For example, if @s@ is a stream of type @Stream (Array --- '5 Word8)@, then @s .!! 3@ has type @Stream Word8@ and contains the 3rd --- element (starting from zero) of the arrays in @s@ at any point in time. -{-# DEPRECATED (.!!) "This function is deprecated in Copilot 4. Use (!)." #-} -(.!!) :: ( KnownNat n - , Typed t - ) => Stream (Array n t) -> Stream Word32 -> Stream t -(.!!) = (!) - -- | Pair a stream with an element accessor, without applying it to obtain the -- value of the element. -- From 7414eba08898ce33c4394370ceaa1702779c6aad Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 03:29:04 -0800 Subject: [PATCH 176/483] copilot-libraries: Remove deprecated function Copilot.Library.Utils.(!!). Refs #599. The function `copilot-libraries:Copilot.Library.Utils.(!!)` was deprecated due to the introduction of a new, common interface to interact with structures and arrays. The original function was deprecated in Copilot 4 and no messages have been received requesting that it be kept in this library. Per our internal policy of waiting 3 versions from deprecation until a public interface declaration can be removed, this function can now be removed. This commit removes the function `copilot-libraries:Copilot.Library.Utils.(!!)`, and adjusts any imports that were hiding either the same function or equally named functions to avoid clashes, since they would no longer occur. --- copilot-libraries/src/Copilot/Library/Libraries.hs | 2 +- copilot-libraries/src/Copilot/Library/Utils.hs | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/copilot-libraries/src/Copilot/Library/Libraries.hs b/copilot-libraries/src/Copilot/Library/Libraries.hs index 6a217234c..f378063bb 100644 --- a/copilot-libraries/src/Copilot/Library/Libraries.hs +++ b/copilot-libraries/src/Copilot/Library/Libraries.hs @@ -23,6 +23,6 @@ import Copilot.Library.LTL import Copilot.Library.PTLTL import Copilot.Library.Statistics import Copilot.Library.RegExp -import Copilot.Library.Utils hiding ((!!)) +import Copilot.Library.Utils import Copilot.Library.Voting import Copilot.Library.Stacks diff --git a/copilot-libraries/src/Copilot/Library/Utils.hs b/copilot-libraries/src/Copilot/Library/Utils.hs index 0200778a4..6785aa88d 100644 --- a/copilot-libraries/src/Copilot/Library/Utils.hs +++ b/copilot-libraries/src/Copilot/Library/Utils.hs @@ -13,10 +13,10 @@ module Copilot.Library.Utils -- ** Scans nscanl, nscanr, nscanl1, nscanr1, -- ** Indexing - case', (!!), (!!!)) + case', (!!!)) where -import Copilot.Language hiding ((!!)) +import Copilot.Language import qualified Prelude as P -- | Given a stream, produce an infinite list of streams dropping an increasing @@ -131,13 +131,6 @@ case' predicates alternatives = P.++ "greater by one than the length of predicates list" in case'' predicates alternatives --- | Index. --- --- WARNING: Very expensive! Consider using this only for very short lists. -(!!) :: (Typed a, Eq b, Num b, Typed b) => [Stream a] -> Stream b -> Stream a -(!!) = (!!!) -{-# DEPRECATED (!!) "This function is deprecated in Copilot 4. Use (!!!)." #-} - -- | Index. -- -- WARNING: Very expensive! Consider using this only for very short lists. From f5bad968ab441339d9cca2672baf7ee857499b50 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 03:46:56 -0800 Subject: [PATCH 177/483] copilot-language: Document changes in CHANGELOG. Refs #599. --- copilot-language/CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 2aa4f6008..15466b154 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,6 +1,8 @@ -2025-02-28 +2025-03-07 * Fix typo in documentation. (#587) * Record how a Property's underlying proposition is quantified. (#254) + * Remove deprecated function Copilot.Language.Operators.Array.(.!!). + (#599) 2025-01-07 * Version bump (4.2). (#577) From 7d22a1b2f5f496bb5cc7e93e881ad36bc3e8a347 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 03:47:28 -0800 Subject: [PATCH 178/483] copilot-libraries: Document changes in CHANGELOG. Refs #599. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index bd1a6b88c..86c3318cd 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-03-07 + * Remove deprecated function Copilot.Library.Utils.(!!). (#599) + 2025-01-07 * Version bump (4.2). (#577) * Bump upper version constraint on containers. (#570) From bfc176a20aedc16dfe8083931fd582ffe7812631 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 17:11:08 -0800 Subject: [PATCH 179/483] Bump versions and constraints. Refs #604. --- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 4 ++-- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot/copilot.cabal | 14 +++++++------- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index a693b60f4..48fc6aa36 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.2 +version : 4.3 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.2 && < 4.3 + , copilot-core >= 4.3 && < 4.4 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 963097d1e..1140f129e 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.2 +version: 4.3 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 68a0316f9..835f27106 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.2 +version: 4.3 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.2 && < 4.3 + copilot-core >= 4.3 && < 4.4 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 5a6a8adb9..53c981618 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.2 +version: 4.3 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.2 && < 4.3 - , copilot-interpreter >= 4.2 && < 4.3 - , copilot-theorem >= 4.2 && < 4.3 + , copilot-core >= 4.3 && < 4.4 + , copilot-interpreter >= 4.3 && < 4.4 + , copilot-theorem >= 4.3 && < 4.4 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 9d6a52ae9..737a590e7 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.2 +version: 4.3 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.2 && < 4.3 + , copilot-language >= 4.3 && < 4.4 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index ab5e63283..46e8de208 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.2 +version: 4.3 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.2 && < 4.3 + copilot-core >= 4.3 && < 4.4 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index c3d2496dc..c6994913f 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.2 +version : 4.3 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 4.2 && < 4.3 - , copilot-prettyprinter >= 4.2 && < 4.3 + , copilot-core >= 4.3 && < 4.4 + , copilot-prettyprinter >= 4.3 && < 4.4 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 272a3edb8..a39788ab8 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.2 +version: 4.3 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.2 && < 4.3 - , copilot-theorem >= 4.2 && < 4.3 - , copilot-language >= 4.2 && < 4.3 - , copilot-libraries >= 4.2 && < 4.3 - , copilot-c99 >= 4.2 && < 4.3 - , copilot-prettyprinter >= 4.2 && < 4.3 + , copilot-core >= 4.3 && < 4.4 + , copilot-theorem >= 4.3 && < 4.4 + , copilot-language >= 4.3 && < 4.4 + , copilot-libraries >= 4.3 && < 4.4 + , copilot-c99 >= 4.3 && < 4.4 + , copilot-prettyprinter >= 4.3 && < 4.4 exposed-modules: Language.Copilot, Language.Copilot.Main From 04f7bc4d6d78f07ae0a710a50f18c12074f1ed80 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Mar 2025 17:12:58 -0800 Subject: [PATCH 180/483] Document changes in CHANGELOG. Refs #604. --- copilot-c99/CHANGELOG | 3 +++ copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 1 + copilot-libraries/CHANGELOG | 1 + copilot-prettyprinter/CHANGELOG | 3 ++- copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 16 insertions(+), 4 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 84254f6e6..48f275cf7 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2025-03-07 + * Version bump (4.3). (#604) + 2025-01-07 * Version bump (4.2). (#577) * Remove uses of Copilot.Core.Expr.UExpr.uExprType. (#565) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index dc86d9739..371c8c2d9 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2025-02-28 +2025-03-07 + * Version bump (4.3). (#604) * Fix typo in documentation. (#587) * Add a Show instance for Type. (#589) * Add a Prop type to capture how a property is quantified. (#254) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 46c8f0c6b..924fa7436 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-03-07 + * Version bump (4.3). (#604) + 2025-01-07 * Version bump (4.2). (#577) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 15466b154..2f729caac 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ 2025-03-07 + * Version bump (4.3). (#604) * Fix typo in documentation. (#587) * Record how a Property's underlying proposition is quantified. (#254) * Remove deprecated function Copilot.Language.Operators.Array.(.!!). diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 86c3318cd..228e37a5d 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ 2025-03-07 + * Version bump (4.3). (#604) * Remove deprecated function Copilot.Library.Utils.(!!). (#599) 2025-01-07 diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index f930b53d0..caec436ef 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,4 +1,5 @@ -2025-02-28 +2025-03-07 + * Version bump (4.3). (#604) * Update pretty-printing to handle Props. (#254) 2025-01-07 diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 1c76ef96a..d3c2bd464 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2025-02-28 +2025-03-07 + * Version bump (4.3). (#604) * Fix multiple typos in README. (#560) * Fix typo in documentation. (#587) * Add function to produce counterexamples for invalid properties. (#589) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 4961ed466..abd146bba 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2025-03-05 +2025-03-07 + * Version bump (4.3). (#604) * Include missing dependencies in installation instructions. (#591) * Update version of GHC in README. (#590) * Add example of how to use proveWithCounterExample. (#589) From 007e80aabd893a3fa2b57c17f58f6530b00530bf Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 21 Feb 2025 16:22:12 -0500 Subject: [PATCH 181/483] copilot-theorem: Add Props to specProps. Refs #594. `copilot-theorem`'s Kind2 backend takes a proposition and attempts to prove that it is valid at all possible time steps. Currently, this happens regardless of whether the proposition is universally or existentially quantified. This is unsound, and it can lead to valid, existentially quantified properties being incorrectly labeled as invalid. This commit adds information about propositions' quantifiers to the Kind2 backend's implementation so that it may use the quantifiers when translating the properties to Kind2. This is accomplished by updating `specProps` to map each proposition's identifier to its corresponding `copilot-core` `Prop`. --- copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs | 4 ++-- .../src/Copilot/Theorem/TransSys/PrettyPrint.hs | 3 ++- copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs | 4 +++- copilot-theorem/src/Copilot/Theorem/TransSys/Transform.hs | 3 ++- copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs | 8 +++++--- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs index d8115e893..123f6ed64 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs @@ -65,7 +65,7 @@ trSpec spec predCallsGraph _assumptions checkedProps = K.File preds props preds = map (trNode spec predCallsGraph) (specNodes spec) props = map trProp $ filter ((`elem` checkedProps) . fst) $ - Map.toList (specProps spec) + Map.toList $ Map.map fst $ specProps spec trProp :: (PropId, ExtVar) -> K.Prop trProp (pId, var) = K.Prop pId (trVar . extVarLocalPart $ var) @@ -98,7 +98,7 @@ addAssumptions spec assumptions (K.File {K.filePreds, K.fileProps}) = vars = let bindings = nodeImportedVars (specTopNode spec) - toExtVar a = fromJust $ Map.lookup a (specProps spec) + toExtVar a = fst $ fromJust $ Map.lookup a $ specProps spec toTopVar (ExtVar nId v) = assert (nId == specTopNodeId spec) v in map (varName . toTopVar . toExtVar) assumptions diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/PrettyPrint.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/PrettyPrint.hs index 89fdc5787..3debe552c 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/PrettyPrint.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/PrettyPrint.hs @@ -27,7 +27,8 @@ pSpec spec = items $$ props items = foldr (($$) . pNode) empty (specNodes spec) props = text "PROPS" $$ Map.foldrWithKey (\k -> ($$) . pProp k) - empty (specProps spec) + empty + (Map.map fst (specProps spec)) pProp pId extvar = quotes (text pId) <+> text "is" <+> pExtVar extvar diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs index d2a953387..e0a297535 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs @@ -24,6 +24,8 @@ module Copilot.Theorem.TransSys.Spec , specDependenciesGraph , specTopNode ) where +import qualified Copilot.Core as C + import Copilot.Theorem.TransSys.Type import Copilot.Theorem.TransSys.Operators import Copilot.Theorem.TransSys.Invariants @@ -55,7 +57,7 @@ type PropId = String data TransSys = TransSys { specNodes :: [Node] , specTopNodeId :: NodeId - , specProps :: Map PropId ExtVar } + , specProps :: Map PropId (ExtVar, C.Prop) } -- | A node is a set of variables living in a local namespace and corresponding -- to the 'Var' type. diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Transform.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Transform.hs index 79edd2828..e59058496 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Transform.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Transform.hs @@ -42,7 +42,8 @@ mergeNodes toMergeIds spec = spec { specNodes = newNode : map (updateOtherNode newNodeId toMergeIds renamingExtF) otherNodes - , specProps = Map.map renamingExtF (specProps spec) } + , specProps = + Map.map (\(ev, prop) -> (renamingExtF ev, prop)) (specProps spec) } where nodes = specNodes spec diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs index 68ddb8806..f15162e49 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs @@ -96,10 +96,12 @@ translate cspec = cprops :: [C.Property] cprops = C.specProperties cspec - propBindings :: Map PropId ExtVar + propBindings :: Map PropId (ExtVar, C.Prop) propBindings = Map.fromList $ do - pid <- map C.propertyName cprops - return (pid, mkExtVar topNodeId pid) + cprop <- cprops + let pid = C.propertyName cprop + prop = C.propertyProp cprop + return (pid, (mkExtVar topNodeId pid, prop)) ((modelNodes, propNodes), extvarNodesNames) = runTrans $ liftM2 (,) (mapM stream (C.specStreams cspec)) (mkPropNodes cprops) From a007efad3207b6603e46da408ebbd3d60e61018b Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 24 Feb 2025 05:41:21 -0500 Subject: [PATCH 182/483] copilot-theorem: Translate quantifiers correctly in Kind2 backend. Refs #594. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `copilot-theorem`'s Kind2 backend takes a proposition and attempts to prove that it is valid at all possible time steps. Currently, this happens regardless of whether the proposition is universally or existentially quantified. This is unsound, and it can lead to valid, existentially quantified properties being incorrectly labeled as invalid. This commit fixes the translations of propositions to Kind2 (via Kind2's TransSys backend) such that the translated properties respect quantifier information. Specifically, this replaces an unsound use of the `extractProp` function with an analysis that translates the property differently depending on whether it is universally or existentially quantified. Because all TransSys propositions use universal quantification, we convert an existentially quantified property `∃x.P(x)` as `¬(∀x.¬(P(x)))` during the translation to TransSys. --- .../src/Copilot/Theorem/Kind2/Output.hs | 29 +++++++++++++++---- .../src/Copilot/Theorem/Kind2/Prover.hs | 18 +++++++++--- .../src/Copilot/Theorem/TransSys/Spec.hs | 3 +- .../src/Copilot/Theorem/TransSys/Translate.hs | 10 ++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs index 42a2f60dc..f80b8170b 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs @@ -8,19 +8,38 @@ import Text.XML.Light hiding (findChild) import Copilot.Theorem.Prove as P import Data.Maybe (fromJust) +import qualified Copilot.Core as C + import qualified Copilot.Theorem.Misc.Error as Err simpleName s = QName s Nothing Nothing -- | Parse output of Kind2. parseOutput :: String -- ^ Property whose validity is being checked. + -> C.Prop -- ^ The property's quantifier. -> String -- ^ XML output of Kind2 -> P.Output -parseOutput prop xml = fromJust $ do +parseOutput propId propQuantifier xml = fromJust $ do root <- parseXMLDoc xml case findAnswer . findPropTag $ root of - "valid" -> return (Output Valid []) - "falsifiable" -> return (Output Invalid []) + "valid" -> case propQuantifier of + -- We encode a universally quantified property P as + -- ∀x.P(x) in Kind2, so the original property is valid + -- iff the Kind2 property is valid. + C.Forall {} -> return (Output Valid []) + -- We encode an existentially quantified property P as + -- ¬(∀x.¬(P(x))) in Kind2, so the original property is + -- invalid iff the Kind2 property is valid. + C.Exists {} -> return (Output Invalid []) + "falsifiable" -> case propQuantifier of + -- We encode a universally quantified property P as + -- ∀x.P(x) in Kind2, so the original property is invalid + -- iff the Kind2 property is invalid. + C.Forall {} -> return (Output Invalid []) + -- We encode an existentially quantified property P as + -- ¬(∀x.¬(P(x))) in Kind2, so the original property is + -- valid iff the Kind2 property is invalid. + C.Exists {} -> return (Output Valid []) s -> err $ "Unrecognized status : " ++ s where @@ -31,10 +50,10 @@ parseOutput prop xml = fromJust $ do let rightElement elt = qName (elName elt) == "Property" && lookupAttr (simpleName "name") (elAttribs elt) - == Just prop + == Just propId in case filterChildren rightElement root of tag : _ -> tag - _ -> err $ "Tag for property " ++ prop ++ " not found" + _ -> err $ "Tag for property " ++ propId ++ " not found" findAnswer tag = case findChildren (simpleName "Answer") tag of diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs index 3931c615b..66db2c346 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs @@ -19,10 +19,12 @@ import Copilot.Theorem.Misc.Utils (openTempFile) import System.Process -import System.Directory -import Data.Default +import System.Directory +import Data.Default +import qualified Data.Map as Map -import qualified Copilot.Theorem.TransSys as TS +import qualified Copilot.Theorem.Misc.Error as Err +import qualified Copilot.Theorem.TransSys as TS -- | Options for Kind2 data Options = Options @@ -68,4 +70,12 @@ askKind2 (ProverST opts spec) assumptions toCheck = do putStrLn kind2Input removeFile tempName - return $ parseOutput (head toCheck) output + + let propId = head toCheck + propQuantifier = case Map.lookup propId (TS.specProps spec) of + Just (_, quantifier) -> + quantifier + Nothing -> + Err.impossible $ + "askKind2: " ++ propId ++ " not in specProps" + return $ parseOutput propId propQuantifier output diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs index e0a297535..b68181ae0 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Spec.hs @@ -53,7 +53,8 @@ type NodeId = String type PropId = String -- | A modular transition system is defined by a graph of nodes and a series --- of properties, each mapped to a variable. +-- of properties, each mapped to a variable and a 'C.Prop' describing how the +-- property is quantified. data TransSys = TransSys { specNodes :: [Node] , specTopNodeId :: NodeId diff --git a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs index f15162e49..2659fa6cb 100644 --- a/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/TransSys/Translate.hs @@ -141,7 +141,15 @@ streamOfProp :: C.Property -> C.Stream streamOfProp prop = C.Stream { C.streamId = 42 , C.streamBuffer = [] - , C.streamExpr = C.extractProp (C.propertyProp prop) + -- TransSys encodes all properties using universal quantification. + -- Therefore, in order to encode an existentially quantified + -- property ∃x.P(x), we must first convert it to ¬(∀x.¬(P(x))). + -- The `Exists` case below handles the ∀x.¬(P(x)) part by adding + -- an `Op1 Not` around the property. The outermost negation is + -- handled elsewhere (e.g., in Copilot.Theorem.Kind2.Output.parse). + , C.streamExpr = case C.propertyProp prop of + C.Forall p -> p + C.Exists p -> C.Op1 C.Not p , C.streamExprType = C.Bool } stream :: C.Stream -> Trans Node From 3b51c969d863755431cb628de80bc02e0c243a8d Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 7 Mar 2025 07:08:02 -0500 Subject: [PATCH 183/483] copilot-theorem: Document changes in CHANGELOG. Refs #594. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index d3c2bd464..8416d3a75 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2025-04-17 + * Translate quantifiers correctly in Kind2 backend. (#594) + 2025-03-07 * Version bump (4.3). (#604) * Fix multiple typos in README. (#560) From 03b7adc2bc7d119f1d6214166208071378f43d41 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 21 Apr 2025 09:44:38 -0700 Subject: [PATCH 184/483] copilot-core: Remove deprecated Copilot.Core.Type.uTypeType. Refs #615. The record field uTypeType, part of Copilot.Core.Type.UType, is never really used within the module or in any other part of Copilot or, as far as we know, by any user of Copilot. The record field was deprecated in Copilot 4.1 and no messages have been received requesting that it be kept in this library. As per our internal policy of waiting 3 versions from deprecation until a public interface declaration can be removed, this definition can now be removed. This commit removes the field uTypeType from the definition of Copilot.Core.Type.UType, making the type no longer a record. --- copilot-core/src/Copilot/Core/Type.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Type.hs b/copilot-core/src/Copilot/Core/Type.hs index 8d54c82f3..b290810c2 100644 --- a/copilot-core/src/Copilot/Core/Type.hs +++ b/copilot-core/src/Copilot/Core/Type.hs @@ -289,8 +289,7 @@ instance (Typeable t, Typed t, KnownNat n) => Typed (Array n t) where simpleType (Array t) = SArray t -- | A untyped type (no phantom type). -data UType = forall a . Typeable a => UType { uTypeType :: Type a } -{-# DEPRECATED uTypeType "This field is deprecated in Copilot 4.1. Use pattern matching instead." #-} +data UType = forall a . Typeable a => UType (Type a) instance Eq UType where UType ty1 == UType ty2 = typeRep ty1 == typeRep ty2 From 3e0b13d8a1b4f9317759a11fc4a4c65056cb9033 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 21 Apr 2025 09:46:50 -0700 Subject: [PATCH 185/483] copilot-core: Document changes in CHANGELOG. Refs #615. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 371c8c2d9..9ae7dd071 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2025-04-21 + * Remove deprecated Copilot.Core.Type.uTypeType. (#615) + 2025-03-07 * Version bump (4.3). (#604) * Fix typo in documentation. (#587) From bb3a109c682b642eb40343746c255aaa177fbe20 Mon Sep 17 00:00:00 2001 From: kaBeech Date: Fri, 18 Apr 2025 11:11:42 -0700 Subject: [PATCH 186/483] copilot: Update build status URL. Refs #612. Currently the badge for the build status in the README doesn't show up correctly. GitHub has cached the result but it's not providing the resulting image, so we see "Build Status" instead. This commit updates the badge URL to the current "https://api.travis-ci.com/Copilot-Language/copilot.svg?branch=master", which enables the badge to show properly. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index efdc58d28..893d6a933 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -2,7 +2,7 @@ # Copilot -[![Build Status](https://travis-ci.com/Copilot-Language/copilot.svg?branch=master)](https://app.travis-ci.com/github/Copilot-Language/copilot) +[![Build Status](https://api.travis-ci.com/Copilot-Language/copilot.svg?branch=master)](https://app.travis-ci.com/github/Copilot-Language/copilot) [![Version on Hackage](https://img.shields.io/hackage/v/copilot.svg)](https://hackage.haskell.org/package/copilot) Copilot is a runtime verification framework for hard real-time systems. From e54f7c0f43b8785a0f478988adea289d76ca634f Mon Sep 17 00:00:00 2001 From: kaBeech Date: Fri, 18 Apr 2025 11:34:58 -0700 Subject: [PATCH 187/483] copilot: Document changes in CHANGELOG. Refs #612. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index abd146bba..e39981d15 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-04-18 + * Update build status badge URL. (#612) + 2025-03-07 * Version bump (4.3). (#604) * Include missing dependencies in installation instructions. (#591) From faa9ba72a582da608be6d5d53a351e228cf44bc6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 May 2025 20:21:58 -0700 Subject: [PATCH 188/483] Bump versions and constraints. Refs #618. --- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 4 ++-- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot/copilot.cabal | 14 +++++++------- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 48fc6aa36..dabf04bf0 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.3 +version : 4.4 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.3 && < 4.4 + , copilot-core >= 4.4 && < 4.5 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 1140f129e..daab8383b 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.3 +version: 4.4 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 835f27106..d5b679829 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.3 +version: 4.4 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.3 && < 4.4 + copilot-core >= 4.4 && < 4.5 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 53c981618..87d54101d 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.3 +version: 4.4 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.3 && < 4.4 - , copilot-interpreter >= 4.3 && < 4.4 - , copilot-theorem >= 4.3 && < 4.4 + , copilot-core >= 4.4 && < 4.5 + , copilot-interpreter >= 4.4 && < 4.5 + , copilot-theorem >= 4.4 && < 4.5 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 737a590e7..078d009fd 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.3 +version: 4.4 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.3 && < 4.4 + , copilot-language >= 4.4 && < 4.5 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 46e8de208..2a4a9578f 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.3 +version: 4.4 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.3 && < 4.4 + copilot-core >= 4.4 && < 4.5 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index c6994913f..ad7ab8699 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.3 +version : 4.4 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.7 - , copilot-core >= 4.3 && < 4.4 - , copilot-prettyprinter >= 4.3 && < 4.4 + , copilot-core >= 4.4 && < 4.5 + , copilot-prettyprinter >= 4.4 && < 4.5 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index a39788ab8..6401ef883 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.3 +version: 4.4 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.3 && < 4.4 - , copilot-theorem >= 4.3 && < 4.4 - , copilot-language >= 4.3 && < 4.4 - , copilot-libraries >= 4.3 && < 4.4 - , copilot-c99 >= 4.3 && < 4.4 - , copilot-prettyprinter >= 4.3 && < 4.4 + , copilot-core >= 4.4 && < 4.5 + , copilot-theorem >= 4.4 && < 4.5 + , copilot-language >= 4.4 && < 4.5 + , copilot-libraries >= 4.4 && < 4.5 + , copilot-c99 >= 4.4 && < 4.5 + , copilot-prettyprinter >= 4.4 && < 4.5 exposed-modules: Language.Copilot, Language.Copilot.Main From 2ff7d9f9e4a5f69c04e88d5b67995e5b28da3cdc Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 May 2025 20:23:06 -0700 Subject: [PATCH 189/483] Document changes in CHANGELOG. Refs #618. --- copilot-c99/CHANGELOG | 3 +++ copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 3 +++ copilot-libraries/CHANGELOG | 3 +++ copilot-prettyprinter/CHANGELOG | 3 +++ copilot-theorem/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 ++- 8 files changed, 21 insertions(+), 3 deletions(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 48f275cf7..00f705df8 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-07 + * Version bump (4.4). (#618) + 2025-03-07 * Version bump (4.3). (#604) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 9ae7dd071..4a0902c21 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2025-04-21 +2025-05-07 + * Version bump (4.4). (#618) * Remove deprecated Copilot.Core.Type.uTypeType. (#615) 2025-03-07 diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 924fa7436..c5f4894c8 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-07 + * Version bump (4.4). (#618) + 2025-03-07 * Version bump (4.3). (#604) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 2f729caac..fc28f01f1 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-07 + * Version bump (4.4). (#618) + 2025-03-07 * Version bump (4.3). (#604) * Fix typo in documentation. (#587) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 228e37a5d..a0c598f54 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-07 + * Version bump (4.4). (#618) + 2025-03-07 * Version bump (4.3). (#604) * Remove deprecated function Copilot.Library.Utils.(!!). (#599) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index caec436ef..360d2e9a0 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-07 + * Version bump (4.4). (#618) + 2025-03-07 * Version bump (4.3). (#604) * Update pretty-printing to handle Props. (#254) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 8416d3a75..f221aec44 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2025-04-17 +2025-05-07 + * Version bump (4.4). (#618) * Translate quantifiers correctly in Kind2 backend. (#594) 2025-03-07 diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index e39981d15..b38dd2c3d 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2025-04-18 +2025-05-07 + * Version bump (4.4). (#618) * Update build status badge URL. (#612) 2025-03-07 From 39955b81ca3a2ed84702be73ebb3e67baeb9af06 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 28 May 2025 15:14:33 -0400 Subject: [PATCH 190/483] copilot-bluespec: Include in mainline copilot repo. Refs #623. The `copilot-bluespec` package was originally developed in an external repo (https://github.com/Copilot-Language/copilot-bluespec), but there is interest in having all Copilot projects and libraries merged into the main https://github.com/Copilot-Language/copilot repo for ease of management. This commit includes the last state of the old `copilot-bluespec` repo into the main Copilot repo as a new subdirectory, disregarding the commit history. --- copilot-bluespec/.dockerignore | 29 + copilot-bluespec/CHANGELOG | 35 + copilot-bluespec/DESIGN.md | 928 +++++++++++ copilot-bluespec/Dockerfile | 36 + copilot-bluespec/LICENSE | 29 + copilot-bluespec/README.md | 57 + copilot-bluespec/Setup.hs | 2 + copilot-bluespec/copilot-bluespec.cabal | 100 ++ .../src/Copilot/Compile/Bluespec.hs | 12 + .../src/Copilot/Compile/Bluespec/CodeGen.hs | 262 +++ .../src/Copilot/Compile/Bluespec/Compile.hs | 350 ++++ .../src/Copilot/Compile/Bluespec/Error.hs | 20 + .../src/Copilot/Compile/Bluespec/Expr.hs | 671 ++++++++ .../src/Copilot/Compile/Bluespec/External.hs | 57 + .../Copilot/Compile/Bluespec/FloatingPoint.hs | 171 ++ .../src/Copilot/Compile/Bluespec/Name.hs | 95 ++ .../Compile/Bluespec/Representation.hs | 22 + .../src/Copilot/Compile/Bluespec/Settings.hs | 14 + .../src/Copilot/Compile/Bluespec/Type.hs | 95 ++ copilot-bluespec/tests/Main.hs | 18 + .../tests/Test/Copilot/Compile/Bluespec.hs | 1470 +++++++++++++++++ .../Test/Copilot/Compile/Bluespec/External.hs | 63 + 22 files changed, 4536 insertions(+) create mode 100644 copilot-bluespec/.dockerignore create mode 100644 copilot-bluespec/CHANGELOG create mode 100644 copilot-bluespec/DESIGN.md create mode 100644 copilot-bluespec/Dockerfile create mode 100644 copilot-bluespec/LICENSE create mode 100644 copilot-bluespec/README.md create mode 100644 copilot-bluespec/Setup.hs create mode 100644 copilot-bluespec/copilot-bluespec.cabal create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Error.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/External.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/FloatingPoint.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Representation.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Settings.hs create mode 100644 copilot-bluespec/src/Copilot/Compile/Bluespec/Type.hs create mode 100644 copilot-bluespec/tests/Main.hs create mode 100644 copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs create mode 100644 copilot-bluespec/tests/Test/Copilot/Compile/Bluespec/External.hs diff --git a/copilot-bluespec/.dockerignore b/copilot-bluespec/.dockerignore new file mode 100644 index 000000000..d1a0321bc --- /dev/null +++ b/copilot-bluespec/.dockerignore @@ -0,0 +1,29 @@ +dist +dist-* +cabal-dev +*.o +*.hi +*.chi +*.chs.h +*.dyn_o +*.dyn_hi +.hpc +.hsenv +.cabal-sandbox/ +cabal.sandbox.config +*.prof +*.aux +*.hp +*.eventlog +.stack-work/ +cabal.project.local +cabal.project.local~ +.HTF/ +.ghc.environment.* +copilot-profiling +.DS_Store +.log + +Dockerfile +.git +.gitignore diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG new file mode 100644 index 000000000..e062468a0 --- /dev/null +++ b/copilot-bluespec/CHANGELOG @@ -0,0 +1,35 @@ +2025-05-08 + * Version bump (4.4). (#43) + +2025-05-05 + * Version bump (4.3.1). (#41) + * Move Copilot.Compile.Bluespec.External out of shared directory. (#36) + * Allow building with filepath-1.5.*. (#39) + +2025-03-10 + * Version bump (4.3). (#34) + +2025-01-20 + * Version bump (4.2). (#31) + * Implement missing floating-point operations. (#28) + * Allow using same trigger name in multiple declarations. (#30) + +2024-11-08 + * Version bump (4.1). (#25) + +2024-09-09 + * Version bump (4.0). (#23) + * Support translating Copilot array updates to Bluespec. (#19) + +2024-08-03 + * Test GHC 9.4 through 9.8. (#20) + +2024-07-11 + * Version bump (3.20). (#11) + * Support translating Copilot struct updates to Bluespec (#10). + * Fix a bug in the translation of the signum function. (#14) + * Fix a bug in the translation of floating-point comparisons. (#15) + +2024-03-08 + * Version bump (3.19). (#5) + * Create new library for Bluespec backend. diff --git a/copilot-bluespec/DESIGN.md b/copilot-bluespec/DESIGN.md new file mode 100644 index 000000000..dffb607ac --- /dev/null +++ b/copilot-bluespec/DESIGN.md @@ -0,0 +1,928 @@ +# The design of `copilot-bluespec` + +This document provides an overview of how the `copilot-bluespec` library +compiles a Copilot specification to a Bluespec program. We assume that you +already have some familiarity with core Copilot concepts such as streams, +triggers, externs, etc. + +## Bluespec overview + +Bluespec is a high-level hardware design language (HDL). Bluespec comes with a +compiler, `bsc`, as well as its own simulator, which allows users to run +Bluespec programs easily. Bluespec also supports compiling to Verilog. + +There are two different syntaxes for the Bluespec language: Bluespec Haskell +(BH) and Bluespec SystemVerilog (BSV). Bluespec Haskell (also known as Bluespec +Classic), the older of the two syntaxes, is a more functional, high-level +syntax that is heavily inspired by the Haskell programming language. Bluespec +SystemVerilog, which was created later, more closely SystemVerilog and targets +hardware design engineers who are more familiar with SystemVerilog's syntax. +Both syntaxes are merely front-ends for the same language, and programs written +in one syntax can be converted to the other. + +The `copilot-bluespec` library compiles to Bluespec Haskell rather than +Bluespec SystemVerilog. This is primarily because BH's Haskell-like syntax is a +closer fit to how Copilot is structured, which makes the design of the compiler +simpler. + +## A small example + +To get a sense for how `copilot-bluespec` works, let us compile a simple +Copilot specification down to Bluespec. Our specification will declare a stream +containing the Fibonacci numbers, along with some triggers that will fire if a +Fibonacci number is even or odd: + +```hs +module Main (main) where + +import qualified Prelude as P () + +import Language.Copilot +import Copilot.Compile.Bluespec + +fibs :: Stream Word32 +fibs = [1, 1] ++ (fibs + drop 1 fibs) + +evenStream :: Stream Word32 -> Stream Bool +evenStream n = (n `mod` constant 2) == constant 0 + +oddStream :: Stream Word32 -> Stream Bool +oddStream n = not (evenStream n) + +spec :: Spec +spec = do + trigger "even" (evenStream fibs) [arg fibs] + trigger "odd" (oddStream fibs) [arg fibs] + +main :: IO () +main = do + spec' <- reify spec + compile "Fibs" spec' +``` + +Note that the only parts of this program that are `copilot-bluespec`–specific +are: + +1. The `import Copilot.Compile.Bluespec` statement, and +2. The call to `compile "Fibs" spec'` in `main`. This will compile the Copilot + specification to a Bluespec program named `Fibs.bs`. + +Running this program will generate five files[^1]: + +[^1]: The actual code in these files is machine-generated and somewhat +difficult to read. We have cleaned up the code slightly to make it easier to +understand. + +* `FibsTypes.bs`: If the Copilot specification contains any structs, then this + file will contain the corresponding Bluespec struct definitions. The + specification does not contain any structs, so this file is mostly empty: + + ```bluespec + package FibsTypes where + + import FloatingPoint + import Vector + ``` + + Later in this document, we will see a different example that makes use of + structs. Note that the syntax used here is much like in Haskell, with a + notable difference being that Bluespec uses the `package` keyword instead of + `module`. + +* `FibsIfc.bs`: This file defines a _module interface_ `FibsIfc`, whose methods + correspond to the names of the triggers in the Copilot spec: + + ```bluespec + package FibsIfc where + + import FloatingPoint + import Vector + + import FibsTypes + + interface FibsIfc = + even :: UInt 32 -> Action + odd :: UInt 32 -> Action + ``` + + An interface like `FibsIfc` can be thought of as a special form of data type + that describes how a module (a hardware object) interacts with the + surrounding environment. In order for an application to make use of a Copilot + monitor, it must instantiate `FibsIfc`'s methods. Each method takes a `UInt + 32` (an unsigned 32-bit integer) as an argument and return an `Action`, which + is the Bluespec type for expressions that act on the state of the circuit (at + circuit execution time). Possible `Action`s include reading from and writing + to registers, as well as printing messages. + + We will see an example of how to instantiate `FibsIfc` later. + +* `Fibs.bs`: This file defines a `mkFibs` function, which orchestrates + everything in the generated Bluespec monitor: + + ```bluespec + package Fibs where + + import FloatingPoint + import Vector + + import FibsTypes + import FibsIfc + import BluespecFP + + mkFibs :: Module FibsIfc -> Module Empty + mkFibs ifcMod = + module + ifc <- ifcMod + + s0_0 :: Reg (UInt 32) <- mkReg 1 + s0_1 :: Reg (UInt 32) <- mkReg 1 + let s0 :: Vector 2 (Reg (UInt 32)) + s0 = update (update newVector 0 s0_0) 1 s0_1 + s0_idx :: Reg (Bit 64) <- mkReg 0 + + let s0_get :: Bit 64 -> UInt 32 + s0_get x = (select s0 ((s0_idx + x) % 2))._read + + s0_gen :: UInt 32 + s0_gen = s0_get 0 + s0_get 1 + + even_guard :: Bool + even_guard = + (s0_get 0 % 2) == 0 + + odd_guard :: Bool + odd_guard = + not (s0_get 0 % 2 == 0) + + rules + "even": when even_guard ==> + ifc.even (s0_get 0) + + "odd:": when odd_guard ==> + ifc.odd (s0_get 0) + + "step": when True ==> + action + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 + ``` + + `mkFibs` returns a module, which can be thought of as a generator of hardware + objects. `mkFibs` takes a `FibsIfc` module as an argument and returns another + module, which is parameterized by `Empty`. `Empty` is a standard interface + with no methods, and `Empty` is typically used in top-level modules that act + as program entrypoints. + + Note that the `module` and `action` keywords can be thought of as specialized + versions of Haskell's `do`-notation, where `module` denotes the `Module` + monad, and `action` denotes the `ActionValue` monad. (Note that `Action` is + an alias for `ActionValue ()`.) The `Monad` monad describes how to elaborate + the structure of a module, whereas the `ActionValue` monad describes the + behavior of a circuit at execution time. + +* `BluespecFP.bsv`: A collection of floating-point operations that leverage BDPI + (Bluespec's foreign-function interface). We will omit the full contents of + this file for brevity, but it will look something like this: + + ```bluespec + import FloatingPoint::*; + + import "BDPI" function Float bs_fp_expf (Float x); + import "BDPI" function Double bs_fp_exp (Double x); + import "BDPI" function Float bs_fp_logf (Float x); + import "BDPI" function Double bs_fp_log (Double x); + ... + ``` + + For more information on what this file does, see the "Floating-point numbers" + section below. + +* `bs_fp.c`: A collection of floating-point operations implemented in C. These + functions are imported via BDPI in `BluespecFP.bsv`. We will omit the full + contents of this file for brevity, but it will look something like this: + + ```bluespec + #include + + union ui_float { + unsigned int i; + float f; + }; + + union ull_double { + unsigned long long i; + double f; + }; + + unsigned int bs_fp_expf(unsigned int x) { + ... + } + + unsigned long long bs_fp_exp(unsigned long long x) { + ... + } + + unsigned int bs_fp_logf(unsigned int x) { + ... + } + + unsigned long long bs_fp_log(unsigned long long x) { + ... + } + + ... + ``` + + For more information on what this file does, see the "Floating-point numbers" + section below. + +In a larger application, a Copilot user would instantiate `mkFibs` with a +`FibsIfc` module that describes what should happen when the `even` and `odd` +triggers fire. `FibsIfc` contains everything that the user must supply; +everything else is handled within the module that `mkFibs` returns. + +Here is an example of a larger application might look like: + +```bluespec +package Top where + +import Fibs +import FibsIfc +import FibsTypes + +fibsIfc :: Module FibsIfc +fibsIfc = + module + interface + even x = + $display "Even Fibonacci number: %0d" x + + odd x = + $display "Odd Fibonacci number: %0d" x + +mkTop :: Module Empty +mkTop = mkFibs fibsIfc +``` + +`mkTop` is the top-level module that we will use as a program entrypoint. The +only interesting thing that it does is instantiate `mkFibs` with a custom +`FibsIfc`, where `even` and `odd` are defined to display a custom message +whenever an even or odd Fibonacci number is encountered, respectively. + +We can run `mkTop` by using Bluespec's simulator like so: + +``` +$ bsc -sim -g mkTop -u Top.bs +checking package dependencies +compiling Top.bs +code generation for mkTop starts +Elaborated module file created: mkTop.ba +All packages are up to date. + +$ bsc -sim -e mkTop -o mkTop.exe bs_fp.c +Bluesim object created: mkTop.{h,o} +Bluesim object created: model_mkTop.{h,o} +Simulation shared library created: mkTop.exe.so +Simulation executable created: mkTop.exe + +$ ./mkTop.exe -m 10 +Odd Fibonacci number: 1 +Odd Fibonacci number: 1 +Even Fibonacci number: 2 +Odd Fibonacci number: 3 +Odd Fibonacci number: 5 +Even Fibonacci number: 8 +Odd Fibonacci number: 13 +Odd Fibonacci number: 21 +Even Fibonacci number: 34 +``` + +We pass `-m 10` to instruct Bluespec's simulator to only run for 10 clock +cycles. Note that the first clock cycle does not fire any rules. This is a +quirk of how hardware works: the reset signal is off in the first cycle, and +the values of registers do not become ready until after the reset signal is +enabled. Because all of the rules depend on registers, none of them will fire +until the second clock cycle or later. + +## Streams + +Much like in `copilot-c99`, `copilot-bluespec` translates each stream +declaration into a ring buffer. More concretely, it translates a `Stream t` +into a `Vector n (Reg t)`, where: + +* A `Vector` is an array whose length is encoded at the type level, must + like Copilot's arrays. (Note that Bluespec has a separate `Array` type, but + `Array`s are more low-level, and they are not indexed by their length at the + type level.) + +* `n` is the minimum number of elements needed to compute later values in the + stream. + +* `t` is the stream's element type. + +* `Reg` is a register, which stores a value that can be read from and written + to. As time advances, we will update the `Reg`s in the ring buffer with later + values in the stream. + +(Commentary: a ring buffer is not the only way we could translate a stream to +Bluespec. Bluespec also has a +[`MIMO`](https://github.com/B-Lang-org/bsc/blob/f00d205867eefe09c60e11b4df155bb87041799a/src/Libraries/Base3-Misc/MIMO.bsv#L52-L73) +(many-in, many-out) queue that is _almost_ suitable for our needs, but it comes +with an unusual restriction that it must have a minimum size of 2 elements. +There exist Copilot streams that only require one element of storage, so we +would have to special-case these streams if we wanted to use a `MIMO`.) + +The `Fibs.bs` example above contains exactly one stream, which is created at +the top of the `mkFibs` function: + +```bluespec + s0_0 :: Reg (UInt 32) <- mkReg 1 + s0_1 :: Reg (UInt 32) <- mkReg 1 + let s0 :: Vector 2 (Reg (UInt 32)) + s0 = update (update newVector 0 s0_0) 1 s0_1 + s0_idx :: Reg (Bit 64) <- mkReg 0 +``` + +Here, `s0_idx`, tracks the index of the next stream element to be updated. +`mkFibs` then defines several functions in terms of `s0` and `s0_idx`, which +are then used in the rules, which we will describe later. Note that `s0_idx` is +a register of type `Bit 64`, which can be thought of as a raw 64-bit value. + +In order to access an element of a stream, we make use of the `s0_get` +function: + +```bluespec + let s0_get :: Bit 64 -> UInt 32 + s0_get x = (select s0 ((s0_idx + x) % 2))._read +``` + +This will use `s0_idx` and an offset `x` to compute which `Reg` in the `Vector` +to read from. + +## Rules + +The _rules_ govern what actions are performed during each cycle. There are +three actions in the `Fibs.hs` example: + +```bluespec + rules + "even": when even_guard ==> + ifc.even (s0_get 0) + + "odd:": when odd_guard ==> + ifc.odd (s0_get 0) + + "step": when True ==> do + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 +``` + +Each rule consists of three parts: + +1. A label (e.g., `"even"`) that uniquely identifies the rule within the + module. + +2. An explicit condition, which is a boolean expression of the form + `when ==> ...`. In order for a rule to fire on a given clock cycle, + its explicit condition `` must hold. + +3. A rule body (e.g., `ifc.even (s0_get 0)`), which describes what action the + rule performs if it fires on a given clock cycle. + +In the example above, the `"even"` and `"odd"` rules govern the behavior of the +triggers in the Copilot specification. These rules will only fire if +`even_guard` or `odd_guard` hold, and if they fire, they will call the `even` +or `odd` method of `FibsIfcs`, respectively. The definitions of `even_guard` +and `odd_guard` are: + +```bluespec + even_guard :: Bool + even_guard = + (s0_get 0 % 2) == 0 + + odd_guard :: Bool + odd_guard = + not (s0_get 0 % 2 == 0) +``` + +The `"step"` rule is the heart of the Copilot specification, and it always runs +on each clock cycle. `"step"` does two things: + +1. It computes the next element of the `s0` stream using the `s0_gen` function, + which is defined like so: + + ```bluespec + s0_gen :: UInt 32 + s0_gen = s0_get 0 + s0_get 1 + ``` + +2. It increments `s0_idx`, making sure to wrap around to `0` if its value + exceeds `1`. + +### A note on atomicity + +Bluespec rules are _atomic_, which means that the Bluespec compiler will ensure +that the rules are executed in some logical order that ensures the absence of +race conditions. Moreover, all register updates that occur within a module's +rules are performed simultaneously at the end of a clock cycle. + +This might seem strange if you are accustomed to the execution model of a +language like C, where all statements (and their accompanying side effects) are +invoked sequentially, one after the other. This is not how Bluespec works, +however. Recall the definitions of the three rules in the example program +above: + +```bluespec + rules + "even": when even_guard ==> + ifc.even (s0_get 0) + + "odd:": when odd_guard ==> + ifc.odd (s0_get 0) + + "step": when True ==> do + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 +``` + +If this were a language like C, then the order in which the rules appear would +have an effect on the runtime semantics of the program, as the behavior of +`s0_get` (used in the `"even"` and `"odd"` rules) depends on the current value +of `s0` and `s0_idx`. In Bluespec, however, the order of these rules do _not_ +matter. The `"step"` rule performs side effects of updating the value of `s0` +and `s0_idx`, but these effects are performed in a single, atomic transaction +at the end of the clock cycle. This means that the values of `s0` and `s0_idx` +that `s0_get` sees will always match their values at the start of the clock +cycle, regardless of what order Bluespec uses to invoke the rules. + +Because the Bluespec compiler can pick whatever rule order it wishes to, this +can impact the order in which `$display` output is presented. This doesn't +impact the example above, as the `"even"` and `"odd"` rules will never fire on +the same clock cycle, meaning that there is always only one `$display` call per +cycle. If program fires multiple rules on a clock cycle and each one calls +`$display`, then the order in which the `$display` calls will run is not +specified. + +## External streams + +The example above sampled data from a stream that was defined within the +Copilot specification. To see what happens when a specification uses an +external stream, let's make one small change to the example: + +```diff + fibs :: Stream Word32 +-fibs = [1, 1] ++ (fibs + drop 1 fibs) ++fibs = extern "fibs" Nothing +``` + +Now let's re-run the example and inspect the resulting Bluespec program. There +are two notable changes worth commenting on. The first change is in +`FibsIfc.bs`: + +```bluespec +package FibsIfc where + +import FloatingPoint +import Vector + +import FibsTypes + +interface FibsIfc = + even :: UInt 32 -> Action + odd :: UInt 32 -> Action + fibs :: Reg (UInt 32) +``` + +This time, the `FibsIfc` interface has an additional `fibs` method of type `Reg +(UInt 32)`. This register corresponds to the external stream that we just +defined, and it is the responsibility of the application which instantiates +`FibsIfc` to dictate what values should be written to the register. + +The second change is in `Fibs.bs`: + +```bluespec +package Fibs where + +import FloatingPoint +import Vector + +import FibsTypes +import FibsIfc + +mkFibs :: Module FibsIfc -> Module Empty +mkFibs ifcMod = + module + ifc <- ifcMod + + let even_guard :: Bool + even_guard = + (ifc.fibs._read % 2) == 0 + + odd_guard :: Bool + odd_guard = + not (ifc.fibs._read % 2 == 0) + + rules + "even": when even_guard ==> + ifc.even ifc.fibs._read + + "odd:": when odd_guard ==> + ifc.odd ifc.fibs._read +``` + +This time, the only stream that is referenced is `ifc.fibs`. As a consequence, +the `s0` stream, the `s0_idx` index, and the `"step"` rule are no longer +necessary, making the code considerably simpler. + +The flip side to having the generated code be simpler is that the application +which uses `mkFibs` must now do additional work to specify what the value of +`ifc.fibs` is. For example, we can port the behavior of the previous version +of the program like so: + +```bluespec +package Top where + +import Vector + +import Fibs +import FibsIfc +import FibsTypes + +fibsIfc :: Module FibsIfc +fibsIfc = + module + s0_0 :: Reg (UInt 32) <- mkReg 1 + s0_1 :: Reg (UInt 32) <- mkReg 1 + let s0 :: Vector 2 (Reg (UInt 32)) + s0 = update (update newVector 0 s0_0) 1 s0_1 + s0_idx :: Reg (Bit 64) <- mkReg 0 + + let s0_get :: Bit 64 -> UInt 32 + s0_get x = (select s0 ((s0_idx + x) % 2))._read + + s0_gen :: UInt 32 + s0_gen = s0_get 0 + s0_get 1 + + fibs_impl :: Reg (UInt 32) <- mkReg 1 + + interface + even x = + $display "Even Fibonacci number: %0d" x + + odd x = + $display "Odd Fibonacci number: %0d" x + + fibs = fibs_impl + + rules + "fibs": when True ==> + fibs_impl := s0_get 1 + + "step": when True ==> + action + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 + +mkTop :: Module Empty +mkTop = mkFibs fibsIfc +``` + +The code involving `s0`, `s0_idx`, and the `"step"` rule is exactly as before. +The only notable difference is that we create an additional `fibs_impl` +register, declare `fibs = fibs_impl` when instantiating the `FibsIfc` +interface, and declare an additional `"fibs"` rule to update the value of +`fibs_impl` on each clock cycle. Note that we always set the value of +`fibs_impl` to be `s0_get 1`, which corresponds to the most recently computed +Fibonacci number. If we set `fibs_impl` to be `s0_get 0` instead, then +`fibs_impl` would lag behind by one cycle. (Try it.) + +# Notes + +## Arrays + +Copilot's `Array n t` type is translated directly to Bluespec's `Vector n t` +type, which makes life very simple. + +## Structs + +Like Copilot, Bluespec has a notion of structs, e.g., + +```bluespec +struct Coord = + x :: UInt 32 + y :: UInt 32 + +exCoord :: Coord +exCoord = Coord { x = 27, y = 42 } + +flipCoord :: Coord -> Coord +flipCoord c = Coord { x = C.y, y = C.x } +``` + +As such, it proves fairly straightforward to translate Copilot structs to +Bluespec structs. For instance, given these Copilot structs: + +```hs +data Volts = Volts + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + +instance Struct Volts where + typeName _ = "Volts" + toValues volts = [ Value Word16 (numVolts volts) + , Value Bool (flag volts) + ] + +instance Typed Volts where + typeOf = Struct (Volts (Field 0) (Field False)) + +data Battery = Battery + { temp :: Field "temp" Word16 + , volts :: Field "volts" Volts + } + +instance Struct Battery where + typeName _ = "Battery" + toValues battery = [ Value typeOf (temp battery) + , Value typeOf (volts battery) + ] + +instance Typed Battery where + typeOf = Struct (Battery (Field 0) (Field undefined)) +``` + +`copilot-bluespec` will generate the following `StructsTypes.bs` file: + +```bluespec +package StructsTypes where + +import FloatingPoint +import Vector + +struct Volts = + numVolts :: UInt 16 + flag :: Bool + deriving (Bits) + +struct Vattery = + temp :: UInt 16 + volts :: BS_volts + deriving (Bits) +``` + +The purpose of the `*Types.bs` file is to define structs separately from other +parts of the generated code. Note that each struct definition derives a `Bits` +instance so that it can be stored inside a `Reg`. + +## Capitalization of generated names + +Copilot allows users to name several things which will influence the names of +generated code, including external streams, triggers, structs, and the file +names. The `copilot-c99` backend is able to turn just about any user-supplied +name into the name of a C identifier, as C is a remarkably case-insensitive +language. For instance, both "`Volts`" and "`volts`" are legal struct names in +C. + +Bluespec, on the other hand, imposes restrictions on which identifiers can +begin with an uppercase letter and which can begin with a lowercase letter. +The following identifiers must begin with an uppercase letter: + +* Struct names +* Interface names + +The following identifiers must begin with a lowercase letter: + +* Function and value names +* Interface method names + +Package names and rule names may begin with either an uppercase or lowercase +letter. + +How do we ensure that `copilot-bluespec` respects Bluespec's capitalization +requirements? As an example, suppose a Copilot specification defines a struct +named "`volts`". This name cannot be used as a Bluespec struct name on its own, +as it does not begin with an uppercase letter. To avoid issues, we prepend the +prefix "`BS_`" to this name to get "`BS_volts`", which is a valid Bluespec +identifier. Similarly, `copilot-bluespec` will prepend the prefix `bs_` to +uppercase Copilot names if it is used in a place that expects lowercase +Bluespec identifiers. + +Note that `copilot-bluespec` will only prepend prefixes if they are necessary +to prevent the generated code from being littered with `BS_`/`bs_` prefixes in +the common case. + +## Floating-point numbers + +`copilot-bluespec` supports all of Copilot's floating-point operations with +varying degrees of performance. The following floating-point operations compile +directly to relatively performant circuits: + +* Basic arithmetic (`(+)`, `(-)`, `(*)`, `(/)`) +* Equality checking (`(==)` and `(/=)`) +* Inequality checking (`(<)`, `(<=)`, `(>)`, and `(>=)`) +* `abs` +* `signum` +* `recip` + +These operations correspond to the floating-point operations that the Bluespec +standard library provides that are well tested. Unfortunately, the Bluespec +standard library does not offer well-tested versions (or even _any_ versions) +of the remainder of Copilot's floating-point operations. The rest of these +operations are instead implemented by using BDPI (Bluespec's foreign function +interface) to interface with C code: + +* `sqrt` +* `acos` +* `asin` +* `atan` +* `atan2` +* `cos` +* `sin` +* `tan` +* `acosh` +* `asinh` +* `atanh` +* `cosh` +* `sinh` +* `tanh` +* `exp` +* `pow` +* `log` +* `logb` +* `ceiling` +* `floor` + +Implementing these operations via C provides high confidence that they are +implemented correctly, but at a somewhat steep performance penalty. + +Because these operations need to be implemented via BDPI, `copilot-bluespec` +generates two additional files: `BluespecFP.bsv` (which contains the Bluespec +function stubs for each function implemented via BDPI) and `bs_fp.c` (which +contains the corresponding C function definitions). To see how this works, +let us take a look at one of the BDPI'd functions, `sqrt`: + +```bluespec +import "BDPI" function Double bs_fp_sqrt (Double x); +import "BDPI" function Float bs_fp_sqrtf (Float x); +``` + +This declares a Bluespec function `bs_fp_sqrt` that is implemented using a C +function (also of the name `bs_fp_sqrt`) under the hood. This takes a Bluespec +`Double` as an argument and also returns a `Double`. Note that `Double` is not +treated magically by the Bluespec compiler here. This is because any Bluespec +struct can be used in BDPI (provided that the struct type implements the `Bits` +class), and Bluespec's `Double` is implemented as a struct with a `Bits` +instance that exactly matches the bit layout expected by IEEE-754 +double-precision floats. (Similarly for Bluespec's `Float` type.) + +Note that at present, the `import "BDPI"` feature is only available when using +the BSV syntax, not the BH syntax. As such, this is currently the only place +where we generate BSV code. + +The corresponding C code for `bs_fp_sqrt(f)` is: + +```c +union ull_double { + unsigned long long i; + double f; +}; + +union ui_float { + unsigned int i; + float f; +}; + +unsigned long long bs_fp_sqrt(unsigned long long x) { + union ull_double x_u; + union ull_double r_u; + x_u.i = x; + r_u.f = sqrt(x_u.f); + return r_u.i; +} + +unsigned int bs_fp_sqrtf(unsigned int x) { + union ui_float x_u; + union ui_float r_u; + x_u.i = x; + r_u.f = sqrtf(x_u.f); + return r_u.i; +} +``` + +There is a lot to unpack here. Let's go through this step by step: + +1. The C version of `bs_fp_sqrt` takes and returns an `unsigned long long`. The + use of `unsigned long long` is dictated by Bluespec itself: whenever you + use a Bluespec type in BDPI that fits in exactly 64 bits, then Bluespec + expects the corresponding C type to be `unsigned long long`. (You can see + this for yourself by inspecting the generated `imported_BDPI_functions.h` + header file.) + + There is a similar story for `bs_fp_sqrtf`, which takes an `unsigned int`. + Bluespec dictates the use of `unsigned int` when the BDPI types fits in + exactly 32 bits. + +2. This poses something of a challenge for us, since we want the implementation + of `bs_fp_sqrt` to work over `double`s, not `unsigned long long`s. To make + this possible, we define a `union ull_double` type that allows easily + converting an `unsigned long long` to a `double` and vice versa. + + There is an analogous story for `ui_float`, which allows conversion to and + from the `unsigned int` and `float` types. + +3. Finally, we perform the `sqrt(f)` function on the argument, using + `ull_double`/`ui_float` as necessary to make the types work out. + +Strictly speaking, it is only necessary to compile the generated `bs_fp.c` file +if the generated Bluespec program makes use of any of the BDPI-related +floating-point operations mentioned above. That being said, it doesn't hurt to +compile it even if the generated Bluespec program _doesn't_ use any of them, so +it's generally good practice to pass `bs_fp.c` to `bsc`. + +Eventually, we would like to stop using BDPI in favor of native Bluespec code, +which would be more performant. To do so, would we need to address the +following Bluespec issues: + +* The implementation of `sqrt` in Bluespec's standard library is buggy: + https://github.com/B-Lang-org/bsc/issues/710 + +* Bluespec's standard library does not implement the remaining floating-point + operations at all: https://github.com/B-Lang-org/bsc/issues/368 + +## Warnings + +Code generated by `copilot-bluespec` should always compile, but it is not +currently guaranteed that the code will compile without warnings. This is +because unlike `gcc` or `clang`, the `bsc` compiler produces much more warnings +by default, and in rare cases, you may trigger `bsc` warnings. This section +describes some of these warnings. + +### Rule bodies with no actions (G0023) + +Let's suppose you have a trigger named `nop`, and you instantiate it like so: + +```bluespec +exampleIfc :: Module ExampleIfc +exampleIfc = + module + interface + nop :: Action + nop = return () +``` + +As a result, firing the `nop` trigger will do absolutely nothing. `bsc` takes +notice of this fact and warns you about it: + +``` +Warning: "CopilotTest.bs", line 27, column 8: (G0023) + The body of rule `nop' has no actions. Removing... +``` + +Where `G0023` is a unique code associated with this class of warning. + +### Warning suppression notification (S0080) + +If you find the warnings above to be annoying, `bsc` offers a way to suppress +them with the `-suppress-warnings ::...` flag. +For instance, you can suppress the G0023 warning code by passing: + +``` +-suppress-warnings G0023 +``` + +This does indeed suppress them, but now `bsc` produces _another_ warning: + +``` +Warning: Unknown position: (S0080) + 1 warnings were suppressed. +``` + +That's right, `bsc` warns you about the use of `-suppress-warnings`. That feels +a bit silly to me, but in any case, the new warning comes with its own warning +code, S0080. As a result, you can _really_ suppress warnings by passing this to +`bsc`: + +``` +-suppress-warnings G0023:S0080 +``` + +## `language-bluespec` + +`copilot-bluespec` uses +[`language-bluespec`](https://github.com/GaloisInc/language-bluespec) as a +library for creating Bluespec Haskell ASTs. Most of the source code from +`language-bluespec` is directly based on the code in the Bluespec compiler, +[`bsc`](https://github.com/B-Lang-org/bsc). This is because there is (to our +knowledge) no other existing Haskell library for representing Bluespec Haskell +ASTs, and adapting the code in `bsc` proves to be the most straightforward way +of achieving what `copilot-bluespec` needs. + +It is conceivable that if the Bluespec language evolves in the future, then we +will need to update `language-bluespec` accordingly. This seems somewhat +unlikely, however, as we are generating code in a very simple subset of the +Bluespec language. + +Another possibility is to split out the AST parts of `bsc` into their own +library and maintain them going forward. See +https://github.com/B-Lang-org/bsc/issues/546 for more discussion on this point. diff --git a/copilot-bluespec/Dockerfile b/copilot-bluespec/Dockerfile new file mode 100644 index 000000000..381842442 --- /dev/null +++ b/copilot-bluespec/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:focal + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install --yes curl gcc g++ git libz-dev make libgmp3-dev libtcl8.6 libtinfo-dev wget + +ENV BSC_VER=2023.07 +RUN mkdir -p /root/bsc +RUN wget https://github.com/B-Lang-org/bsc/releases/download/$BSC_VER/bsc-$BSC_VER-ubuntu-20.04.tar.gz -O /root/bsc.tar.gz && \ + tar -xvf /root/bsc.tar.gz -C /root/bsc --strip-components=1 && \ + rm /root/bsc.tar.gz + +ENV GHCUP_VER=0.1.22.0 +RUN mkdir -p /root/.ghcup/bin +RUN wget https://downloads.haskell.org/~ghcup/$GHCUP_VER/x86_64-linux-ghcup-$GHCUP_VER -O /root/.ghcup/bin/ghcup && \ + chmod a+x /root/.ghcup/bin/ghcup + +ENV PATH=/root/bsc/bin:/root/.cabal/bin:/root/.ghcup/bin:$PATH + +ENV GHC_VER=9.4.8 +ENV CABAL_VER=3.8.1.0 +RUN ghcup install ghc $GHC_VER && \ + ghcup set ghc $GHC_VER && \ + ghcup install cabal $CABAL_VER && \ + ghcup set cabal $CABAL_VER && \ + cabal update + +COPY . /copilot-bluespec +WORKDIR /copilot-bluespec + +RUN cabal configure --enable-tests && \ + cabal build --write-ghc-environment-files=always && \ + cabal install --lib copilot . --package-env . && \ + cabal test + +ENTRYPOINT ["/usr/bin/bash"] diff --git a/copilot-bluespec/LICENSE b/copilot-bluespec/LICENSE new file mode 100644 index 000000000..d4ed5bc4c --- /dev/null +++ b/copilot-bluespec/LICENSE @@ -0,0 +1,29 @@ +2009 +BSD3 License terms + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the name of the developers nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/copilot-bluespec/README.md b/copilot-bluespec/README.md new file mode 100644 index 000000000..e85d9e8c5 --- /dev/null +++ b/copilot-bluespec/README.md @@ -0,0 +1,57 @@ +[![Build Status](https://github.com/Copilot-Language/copilot-bluespec/workflows/copilot-bluespec/badge.svg)](https://github.com/Copilot-Language/copilot-bluespec/actions?query=workflow%3Acopilot-bluespec) + +# Copilot: a stream DSL +Copilot-Bluespec implements a Bluespec backend for Copilot, producing code that +is suitable for FPGAs. + +Copilot is a runtime verification framework written in Haskell. It allows the +user to write programs in a simple but powerful way using a stream-based +approach. + +Programs can be interpreted for testing, or translated Bluespec code to be +incorporated in a project, or as a standalone application. The Bluespec backend +ensures us that the output is constant in memory and time, making it suitable +for systems with hard realtime requirements. + +## Installation +Copilot-Bluespec can be found on +[Hackage](https://hackage.haskell.org/package/copilot-bluespec). It is intended +to be installed alongside a Copilot distribution by running the following +commands: + +``` +$ cabal update +$ cabal install copilot +$ cabal install copilot-bluespec +``` + +For more detailed instructions on how to install Copilot, please refer to the +[Copilot website](https://copilot-language.github.io). + +The generated Bluespec code requires `bsc` (the Bluespec compiler) in order to +be compiled. `bsc` can be downloaded +[here](https://github.com/B-Lang-org/bsc/releases). + +We also provide a [Dockerfile](Dockerfile) which automates the process of +installing Copilot, Copilot-Bluespec, and `bsc`. The Dockerfile can be built and +run using the following commands: + +``` +$ docker build -t . +$ docker run -it +``` + +Where `` is a unique name for the Docker image. + +## Further information +For further information, install instructions and documentation, please visit +the Copilot website: +[https://copilot-language.github.io](https://copilot-language.github.io) + +There is also an implementation-focused design document +[here](https://raw.githubusercontent.com/Copilot-Language/copilot/master/copilot-bluespec/DESIGN.md). + + +## License +Copilot is distributed under the BSD-3-Clause license, which can be found +[here](https://raw.githubusercontent.com/Copilot-Language/copilot/master/copilot-bluespec/LICENSE). diff --git a/copilot-bluespec/Setup.hs b/copilot-bluespec/Setup.hs new file mode 100644 index 000000000..bf6890196 --- /dev/null +++ b/copilot-bluespec/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain \ No newline at end of file diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal new file mode 100644 index 000000000..6509e7e2a --- /dev/null +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -0,0 +1,100 @@ +cabal-version : >= 1.10 +name : copilot-bluespec +version : 4.4 +synopsis : A compiler for Copilot targeting FPGAs. +description : + This package is a back-end from Copilot to FPGAs in Bluespec. + . + Copilot is a stream (i.e., infinite lists) domain-specific language (DSL) in + Haskell. Copilot contains an interpreter, multiple back-end compilers, and + other verification tools. + . + A tutorial, examples, and other information are available at + . + +license : BSD3 +license-file : LICENSE +maintainer : Ryan Scott +homepage : https://copilot-language.github.io +bug-reports : https://github.com/Copilot-Language/copilot-bluespec/issues +stability : Experimental +category : Language, Embedded +build-type : Simple +extra-source-files : README.md + , CHANGELOG + +author : Frank Dedden + , Alwyn Goodloe + , Ivan Perez + , Ryan Scott + +x-curation: uncurated + +source-repository head + type: git + location: https://github.com/Copilot-Language/copilot-bluespec.git + +library + default-language : Haskell2010 + hs-source-dirs : src + + ghc-options : -Wall + build-depends : base >= 4.9 && < 5 + , directory >= 1.3 && < 1.4 + , filepath >= 1.4 && < 1.6 + , pretty >= 1.1.2 && < 1.2 + + , copilot-core >= 4.4 && < 4.5 + , language-bluespec >= 0.1 && < 0.2 + + exposed-modules : Copilot.Compile.Bluespec + + other-modules : Copilot.Compile.Bluespec.CodeGen + , Copilot.Compile.Bluespec.Compile + , Copilot.Compile.Bluespec.Error + , Copilot.Compile.Bluespec.Expr + , Copilot.Compile.Bluespec.External + , Copilot.Compile.Bluespec.FloatingPoint + , Copilot.Compile.Bluespec.Name + , Copilot.Compile.Bluespec.Representation + , Copilot.Compile.Bluespec.Settings + , Copilot.Compile.Bluespec.Type + +test-suite tests + type: + exitcode-stdio-1.0 + + main-is: + Main.hs + + other-modules: + Test.Copilot.Compile.Bluespec + + Test.Copilot.Compile.Bluespec.External + + build-depends: + base + , directory + , HUnit + , QuickCheck + , extra + , ieee754 + , pretty + , process + , random + , test-framework + , test-framework-hunit + , test-framework-quickcheck2 + , unix + + , copilot-core + , copilot-bluespec + + hs-source-dirs: + tests + + default-language: + Haskell2010 + + ghc-options: + -Wall diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec.hs new file mode 100644 index 000000000..499b1cff6 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec.hs @@ -0,0 +1,12 @@ +-- | Compile Copilot specifications to Bluespec code. +module Copilot.Compile.Bluespec + ( compile + , compileWith + , BluespecSettings(..) + , mkDefaultBluespecSettings + ) where + +-- Internal imports +import Copilot.Compile.Bluespec.Compile ( compile, compileWith ) +import Copilot.Compile.Bluespec.Settings ( BluespecSettings (..), + mkDefaultBluespecSettings ) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs new file mode 100644 index 000000000..6d9fde04d --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs @@ -0,0 +1,262 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | High-level translation of Copilot Core into Bluespec. +module Copilot.Compile.Bluespec.CodeGen + ( -- * Type declarations + mkStructDecln + + -- * Ring buffers + , mkBuffDecln + , mkIndexDecln + , mkAccessDecln + + -- * Stream generators + , mkGenFun + + -- * Monitor processing + , mkStepRule + , mkTriggerRule + + -- * Module interface specifications + , mkSpecIfcFields + ) where + +-- External imports +import Data.String (IsString (..)) +import qualified Language.Bluespec.Classic.AST as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Types as BS + +-- Internal imports: Copilot +import Copilot.Core + +-- Internal imports +import Copilot.Compile.Bluespec.Expr +import Copilot.Compile.Bluespec.External +import Copilot.Compile.Bluespec.Name +import Copilot.Compile.Bluespec.Representation +import Copilot.Compile.Bluespec.Type + +-- | Write a generator function for a stream. +mkGenFun :: String -> Expr a -> Type a -> BS.CDefl +mkGenFun name expr ty = + -- name :: ty + -- name = expr + BS.CLValueSign + (BS.CDef nameId (BS.CQType [] (transType ty)) [def]) + [] + where + nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name + def = BS.CClause [] [] (transExpr expr) + +-- | Bind a buffer variable and initialise it with the stream buffer. +mkBuffDecln :: forall a. Id -> Type a -> [a] -> [BS.CStmt] +mkBuffDecln sId ty xs = + initVals ++ [BS.CSletrec [initBufSig]] + where + -- sId_0 :: Reg <- mkReg xs_0 + -- ... + -- sId_(n-1) :: Reg <- mkReg xs_(n-1) + initVals = zipWith mkInitVal xs [0..] + -- sId :: Vector n (Reg ) + -- sId = update (... (update newVector 0 sId_0) ...) (n-1) sId_(n-1) + initBufSig = BS.CLValueSign + (BS.CDef nameId (BS.CQType [] vecTy) [initBufDef]) + [] + initBufDef = BS.CClause + [] + [] + (genVector + (\idx _ -> BS.CVar $ BS.mkId BS.NoPos $ + fromString $ streamElemName sId idx) + xs) + + nameId = BS.mkId BS.NoPos $ fromString $ streamName sId + bsTy = tReg `BS.TAp` transType ty + vecTy = tVector `BS.TAp` BS.cTNum numElems BS.NoPos `BS.TAp` bsTy + numElems = toInteger $ length xs + + mkInitVal :: a -> Int -> BS.CStmt + mkInitVal x elemNum = + BS.CSBindT + (BS.CPVar elemId) + Nothing + [] + (BS.CQType [] bsTy) + (BS.CApply (BS.CVar (BS.mkId BS.NoPos "mkReg")) [constTy ty x]) + where + elemName = streamElemName sId elemNum + elemId = BS.mkId BS.NoPos $ fromString elemName + +-- | Make an index variable and initialise it to 0. +mkIndexDecln :: Id -> BS.CStmt +mkIndexDecln sId = + -- sId_idx :: Reg (Bit 64) <- mkReg 0 + BS.CSBindT + (BS.CPVar nameId) + Nothing + [] + (BS.CQType [] bsTy) + (BS.CApply (BS.CVar (BS.mkId BS.NoPos "mkReg")) + [cLit $ BS.LInt $ BS.ilDec 0]) + where + nameId = BS.mkId BS.NoPos $ fromString $ indexName sId + bsTy = tReg `BS.TAp` BS.tBitN 64 BS.NoPos + +-- | Define an accessor function for the ring buffer associated with a stream +mkAccessDecln :: Id -> Type a -> [a] -> BS.CDefl +mkAccessDecln sId ty xs = + -- sId_get :: Bits 64 -> ty + -- sId_get x = (select sId ((sId_idx + x) % buffLength))._read + BS.CLValueSign (BS.CDef nameId (BS.CQType [] funTy) [def]) [] + where + def = BS.CClause [BS.CPVar argId] [] expr + argTy = BS.tBit `BS.TAp` BS.cTNum 64 BS.NoPos + retTy = transType ty + funTy = BS.tArrow `BS.TAp` argTy `BS.TAp` retTy + name = streamAccessorName sId + nameId = BS.mkId BS.NoPos $ fromString name + buffLength = cLit $ BS.LInt $ BS.ilDec $ toInteger $ length xs + argId = BS.mkId BS.NoPos "x" + index = BS.CApply (BS.CVar (BS.idPercentAt BS.NoPos)) + [ BS.CApply (BS.CVar BS.idPlus) + [ BS.CVar (BS.mkId BS.NoPos (fromString (indexName sId))) + , BS.CVar argId + ] + , buffLength + ] + indexExpr = cIndexVector + (BS.CVar (BS.mkId BS.NoPos (fromString (streamName sId)))) + index + expr = BS.CSelect indexExpr (BS.id_read BS.NoPos) + +-- | Define fields for a module interface containing a specification's trigger +-- functions and external variables. +mkSpecIfcFields :: [Trigger] -> [External] -> [BS.CField] +mkSpecIfcFields triggers exts = + map mkTriggerField triggers ++ map mkExtField exts + where + -- trigger :: args_1 -> ... -> args_n -> Action + mkTriggerField :: Trigger -> BS.CField + mkTriggerField (Trigger name _ args) = + mkField name $ + foldr + (\(UExpr arg _) res -> BS.tArrow `BS.TAp` transType arg `BS.TAp` res) + BS.tAction + args + + -- ext :: Reg ty + mkExtField :: External -> BS.CField + mkExtField (External name ty) = + mkField name $ tReg `BS.TAp` transType ty + +-- | Define a rule for a trigger function. +mkTriggerRule :: UniqueTrigger -> BS.CRule +mkTriggerRule (UniqueTrigger uniqueName (Trigger name _ args)) = + BS.CRule + [] + (Just $ cLit $ BS.LString uniqueName) + [ BS.CQFilter $ + BS.CVar $ BS.mkId BS.NoPos $ + fromString $ guardName uniqueName + ] + (BS.CApply nameExpr args') + where + ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + -- Note that we use 'name' here instead of 'uniqueName', as 'name' is the + -- name of the actual external function. + nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name + nameExpr = BS.CSelect (BS.CVar ifcArgId) nameId + + args' = take (length args) (map argCall (argNames uniqueName)) + argCall = BS.CVar . BS.mkId BS.NoPos . fromString + +-- | Writes the @step@ rule that updates all streams. +mkStepRule :: [Stream] -> Maybe BS.CRule +mkStepRule streams + | null allUpdates + = -- If there is nothing to update, don't bother creating a step rule. + -- Doing so wouldn't harm anything, but bsc will generate a warning + -- when compiling such an empty rule. + Nothing + | otherwise + = Just $ + BS.CRule + [] + (Just $ cLit $ BS.LString "step") + [BS.CQFilter $ BS.CCon BS.idTrue []] + (BS.Caction BS.NoPos allUpdates) + where + allUpdates = bufferUpdates ++ indexUpdates + (bufferUpdates, indexUpdates) = unzip $ map mkUpdateGlobals streams + + -- Write code to update global stream buffers and index. + mkUpdateGlobals :: Stream -> (BS.CStmt, BS.CStmt) + mkUpdateGlobals (Stream sId buff _ _) = + (bufferUpdate, indexUpdate) + where + bufferUpdate = + BS.CSExpr Nothing $ + BS.Cwrite + BS.NoPos + (cIndexVector (BS.CVar buffId) (BS.CVar indexId)) + (BS.CVar genId) + + indexUpdate = + BS.CSExpr Nothing $ + BS.Cwrite + BS.NoPos + (BS.CVar indexId) + (BS.CApply (BS.CVar (BS.idPercentAt BS.NoPos)) + [incIndex, buffLength]) + where + buffLength = cLit $ BS.LInt $ BS.ilDec $ toInteger $ length buff + incIndex = BS.CApply (BS.CVar BS.idPlus) + [ BS.CVar indexId + , cLit $ BS.LInt $ BS.ilDec 1 + ] + + buffId = BS.mkId BS.NoPos $ fromString $ streamName sId + genId = BS.mkId BS.NoPos $ fromString $ generatorName sId + indexId = BS.mkId BS.NoPos $ fromString $ indexName sId + +-- | Write a struct declaration based on its definition. +mkStructDecln :: Struct a => a -> BS.CDefn +mkStructDecln x = + BS.Cstruct + True + BS.SStruct + (BS.IdK structId) + [] -- No type variables + structFields + -- Derive a Bits instance so that we can put this struct in a Reg + [BS.CTypeclass BS.idBits] + where + structId = BS.mkId BS.NoPos $ fromString $ uppercaseName $ typeName x + structFields = map mkStructField $ toValues x + + mkStructField :: Value a -> BS.CField + mkStructField (Value ty field) = + mkField (fieldName field) (transType ty) + +-- | Write a field of a struct or interface, along with its type. +mkField :: String -> BS.CType -> BS.CField +mkField name ty = + BS.CField + { BS.cf_name = BS.mkId BS.NoPos $ fromString $ lowercaseName name + , BS.cf_pragmas = Nothing + , BS.cf_type = BS.CQType [] ty + , BS.cf_default = [] + , BS.cf_orig_type = Nothing + } + +-- | The @Reg@ Bluespec interface type. +tReg :: BS.CType +tReg = BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.idReg + , BS.tcon_kind = Just (BS.Kfun BS.KStar BS.KStar) + , BS.tcon_sort = BS.TIstruct (BS.SInterface []) + [BS.id_write BS.NoPos, BS.id_read BS.NoPos] + } diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs new file mode 100644 index 000000000..c4701eaf7 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs @@ -0,0 +1,350 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} + +-- | Compile Copilot specifications to Bluespec code. +module Copilot.Compile.Bluespec.Compile + ( compile + , compileWith + ) where + +-- External imports +import Data.List (nub, nubBy, union) +import Data.Maybe (catMaybes, maybeToList) +import Data.String (IsString (..)) +import Data.Type.Equality (testEquality, (:~:)(Refl)) +import Data.Typeable (Typeable) +import qualified Language.Bluespec.Classic.AST as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Types as BS +import Text.PrettyPrint.HughesPJClass (Pretty (..), render) +import System.Directory (createDirectoryIfMissing) +import System.Exit (exitFailure) +import System.FilePath (()) +import System.IO (hPutStrLn, stderr) + +-- Internal imports: Copilot +import Copilot.Core + +-- Internal imports +import Copilot.Compile.Bluespec.CodeGen +import Copilot.Compile.Bluespec.External +import Copilot.Compile.Bluespec.FloatingPoint +import Copilot.Compile.Bluespec.Name +import Copilot.Compile.Bluespec.Representation +import Copilot.Compile.Bluespec.Settings + +-- | Compile a specification to a Bluespec file. +-- +-- The first argument is the settings for the Bluespec code generated. +-- +-- The second argument is used as a module name and the prefix for the .bs files +-- that are generated. +compileWith :: BluespecSettings -> String -> Spec -> IO () +compileWith bsSettings prefix spec + | null triggers + = do hPutStrLn stderr $ + "Copilot error: attempt at compiling empty specification.\n" + ++ "You must define at least one trigger to generate Bluespec monitors." + exitFailure + + | incompatibleTriggers triggers + = do hPutStrLn stderr $ + "Copilot error: attempt at compiling specification with conflicting " + ++ "trigger definitions.\n" + ++ "Multiple triggers have the same name, but different argument " + ++ "types.\n" + exitFailure + + | otherwise + = do let typesBsFile = render $ pPrint $ compileTypesBS bsSettings prefix spec + ifcBsFile = render $ pPrint $ compileIfcBS bsSettings prefix spec + bsFile = render $ pPrint $ compileBS bsSettings prefix spec + + let dir = bluespecSettingsOutputDirectory bsSettings + createDirectoryIfMissing True dir + writeFile (dir specTypesPkgName prefix ++ ".bs") typesBsFile + writeFile (dir specIfcPkgName prefix ++ ".bs") ifcBsFile + writeFile (dir "bs_fp.c") copilotBluespecFloatingPointC + writeFile (dir "BluespecFP.bsv") copilotBluespecFloatingPointBSV + writeFile (dir prefix ++ ".bs") bsFile + where + triggers = specTriggers spec + + -- Check that two triggers do no conflict, that is: if their names are + -- equal, the types of their arguments should be equal as well. + incompatibleTriggers :: [Trigger] -> Bool + incompatibleTriggers = pairwiseAny conflict + where + conflict :: Trigger -> Trigger -> Bool + conflict t1@(Trigger name1 _ _) t2@(Trigger name2 _ _) = + name1 == name2 && not (compareTrigger t1 t2) + + -- True if the function holds for any pair of elements. We assume that + -- the function is commutative. + pairwiseAny :: (a -> a -> Bool) -> [a] -> Bool + pairwiseAny _ [] = False + pairwiseAny _ (_:[]) = False + pairwiseAny f (x:xs) = any (f x) xs || pairwiseAny f xs + +-- | Compile a specification to a Bluespec. +-- +-- The first argument is used as a prefix for the generated .bs files. +compile :: String -> Spec -> IO () +compile = compileWith mkDefaultBluespecSettings + +-- | Generate a @.bs@ file from a 'Spec'. This is the main payload of +-- the Bluespec backend. +-- +-- The generated Bluespec file will import a handful of files from the standard +-- library, as well as the following generated files: +-- +-- * @Ifc.bs@, which defines the interface containing the trigger +-- functions and external variables. +-- +-- * @Types.bs@, which defines any structs used in the 'Spec'. +-- +-- It will also generate a @mk :: Module Ifc -> Module Empty@ +-- function, which defines the module structure for this 'Spec'. The +-- @mk@ function has the following structure: +-- +-- * First, bind the argument of type @Module Ifc@ so that trigger +-- functions can be invoked and external variables can be used. +-- +-- * Next, declare stream buffers and indices. +-- +-- * Next, declare generator functions for streams, accessor functions for +-- streams, and guard functions for triggers. +-- +-- * Next, declare rules for each trigger function. +-- +-- * Finally, declare a single rule that updates the stream buffers and +-- indices. +compileBS :: BluespecSettings -> String -> Spec -> BS.CPackage +compileBS _bsSettings prefix spec = + BS.CPackage + (BS.mkId BS.NoPos (fromString prefix)) + (Right []) + (stdLibImports ++ genImports) + [] + [moduleDef] + [] + where + -- import Types + -- import Ifc + genImports :: [BS.CImport] + genImports = + [ BS.CImpId False $ BS.mkId BS.NoPos $ fromString + $ specTypesPkgName prefix + , BS.CImpId False $ BS.mkId BS.NoPos $ fromString + $ specIfcPkgName prefix + , BS.CImpId False $ BS.mkId BS.NoPos "BluespecFP" + ] + + moduleDef :: BS.CDefn + moduleDef = BS.CValueSign $ + BS.CDef + (BS.mkId BS.NoPos $ fromString $ "mk" ++ prefix) + -- :: Module Ifc -> Module Empty + (BS.CQType + [] + (BS.tArrow + `BS.TAp` (BS.tModule `BS.TAp` ifcTy) + `BS.TAp` (BS.tModule `BS.TAp` emptyTy))) + [ BS.CClause [BS.CPVar ifcModId] [] $ + BS.Cmodule BS.NoPos $ + BS.CMStmt + (BS.CSBind (BS.CPVar ifcArgId) Nothing [] (BS.CVar ifcModId)) + : map BS.CMStmt mkGlobals ++ + [ BS.CMStmt $ BS.CSletrec genFuns + , BS.CMrules $ BS.Crules [] rules + ] + ] + + ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + ifcModId = BS.mkId BS.NoPos "ifcMod" + + rules :: [BS.CRule] + rules = map mkTriggerRule uniqueTriggers ++ maybeToList (mkStepRule streams) + + streams = specStreams spec + triggers = specTriggers spec + uniqueTriggers = mkUniqueTriggers triggers + exts = gatherExts streams triggers + + ifcId = BS.mkId BS.NoPos $ fromString $ specIfcName prefix + ifcFields = mkSpecIfcFields triggers exts + ifcTy = BS.TCon (BS.TyCon + { BS.tcon_name = ifcId + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = BS.TIstruct + (BS.SInterface []) + (map BS.cf_name ifcFields) + }) + + emptyTy = BS.TCon (BS.TyCon + { BS.tcon_name = BS.idEmpty + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = BS.TIstruct (BS.SInterface []) [] + }) + + -- Make buffer and index declarations for streams. + mkGlobals :: [BS.CStmt] + mkGlobals = concatMap buffDecln streams ++ map indexDecln streams + where + buffDecln (Stream sId buff _ ty) = mkBuffDecln sId ty buff + indexDecln (Stream sId _ _ _ ) = mkIndexDecln sId + + -- Make generator functions, including trigger arguments. + genFuns :: [BS.CDefl] + genFuns = map accessDecln streams + ++ map streamGen streams + ++ concatMap triggerGen uniqueTriggers + where + accessDecln :: Stream -> BS.CDefl + accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff + + streamGen :: Stream -> BS.CDefl + streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty + + triggerGen :: UniqueTrigger -> [BS.CDefl] + triggerGen (UniqueTrigger uniqueName (Trigger _name guard args)) = + guardDef : argDefs + where + guardDef = mkGenFun (guardName uniqueName) guard Bool + argDefs = map argGen (zip (argNames uniqueName) args) + + argGen :: (String, UExpr) -> BS.CDefl + argGen (argName, UExpr ty expr) = mkGenFun argName expr ty + +-- | Generate a @Ifc.bs@ file from a 'Spec'. This contains the +-- definition of the @Ifc@ interface, which declares the types of all +-- trigger functions and external variables. This is put in a separate file so +-- that larger applications can use it separately. +compileIfcBS :: BluespecSettings -> String -> Spec -> BS.CPackage +compileIfcBS _bsSettings prefix spec = + BS.CPackage + ifcPkgId + (Right []) + (stdLibImports ++ genImports) + [] + [ifcDef] + [] + where + -- import Types + genImports :: [BS.CImport] + genImports = + [ BS.CImpId False $ BS.mkId BS.NoPos $ fromString + $ specTypesPkgName prefix + ] + + ifcId = BS.mkId BS.NoPos $ fromString $ specIfcName prefix + ifcPkgId = BS.mkId BS.NoPos $ fromString $ specIfcPkgName prefix + ifcFields = mkSpecIfcFields triggers exts + + streams = specStreams spec + exts = gatherExts streams triggers + + -- Remove duplicates due to multiple guards for the same trigger. + triggers = nubBy compareTrigger (specTriggers spec) + + ifcDef :: BS.CDefn + ifcDef = BS.Cstruct + True + (BS.SInterface []) + (BS.IdK ifcId) + [] -- No type variables + ifcFields + [] -- No derived instances + +-- | Generate a @Types.bs@ file from a 'Spec'. This declares the types +-- of any structs used by the Copilot specification. This is put in a separate +-- file so that larger applications can more easily substitute their own struct +-- definitions if desired. +compileTypesBS :: BluespecSettings -> String -> Spec -> BS.CPackage +compileTypesBS _bsSettings prefix spec = + BS.CPackage + typesId + (Right []) + stdLibImports + [] + structDefs + [] + where + typesId = BS.mkId BS.NoPos $ fromString $ specTypesPkgName prefix + + structDefs = mkTypeDeclns exprs + + exprs = gatherExprs streams triggers + streams = specStreams spec + + -- Remove duplicates due to multiple guards for the same trigger. + triggers = nubBy compareTrigger (specTriggers spec) + + -- Generate type declarations. + mkTypeDeclns :: [UExpr] -> [BS.CDefn] + mkTypeDeclns es = catMaybes $ map mkTypeDecln uTypes + where + uTypes = nub $ concatMap (\(UExpr _ e) -> exprTypes e) es + + mkTypeDecln (UType ty) = case ty of + Struct x -> Just $ mkStructDecln x + _ -> Nothing + +-- | Imports from the Bluespec standard library. +stdLibImports :: [BS.CImport] +stdLibImports = + [ BS.CImpId False $ BS.mkId BS.NoPos "FloatingPoint" + , BS.CImpId False $ BS.mkId BS.NoPos "Vector" + ] + +-- ** Obtain information from Copilot Core Exprs and Types. + +-- | List all types of an expression, returns items uniquely. +exprTypes :: Typeable a => Expr a -> [UType] +exprTypes e = case e of + Const ty _ -> typeTypes ty + Local ty1 ty2 _ e1 e2 -> typeTypes ty1 `union` typeTypes ty2 + `union` exprTypes e1 `union` exprTypes e2 + Var ty _ -> typeTypes ty + Drop ty _ _ -> typeTypes ty + ExternVar ty _ _ -> typeTypes ty + Op1 _ e1 -> exprTypes e1 + Op2 _ e1 e2 -> exprTypes e1 `union` exprTypes e2 + Op3 _ e1 e2 e3 -> exprTypes e1 `union` exprTypes e2 + `union` exprTypes e3 + Label ty _ _ -> typeTypes ty + +-- | List all types of a type, returns items uniquely. +typeTypes :: Typeable a => Type a -> [UType] +typeTypes ty = case ty of + Array ty' -> typeTypes ty' `union` [UType ty] + Struct x -> concatMap (\(Value ty' _) -> typeTypes ty') (toValues x) + `union` [UType ty] + _ -> [UType ty] + +-- | Collect all expression of a list of streams and triggers and wrap them +-- into an UEXpr. +gatherExprs :: [Stream] -> [Trigger] -> [UExpr] +gatherExprs streams triggers = map streamUExpr streams + ++ concatMap triggerUExpr triggers + where + streamUExpr (Stream _ _ expr ty) = UExpr ty expr + triggerUExpr (Trigger _ guard args) = UExpr Bool guard : args + +-- | We consider triggers to be equal, if their names match and the number and +-- types of arguments. +compareTrigger :: Trigger -> Trigger -> Bool +compareTrigger (Trigger name1 _ args1) (Trigger name2 _ args2) + = name1 == name2 && compareArguments args1 args2 + + where + compareArguments :: [UExpr] -> [UExpr] -> Bool + compareArguments [] [] = True + compareArguments [] _ = False + compareArguments _ [] = False + compareArguments (x:xs) (y:ys) = compareUExpr x y && compareArguments xs ys + + compareUExpr :: UExpr -> UExpr -> Bool + compareUExpr (UExpr ty1 _) (UExpr ty2 _) + | Just Refl <- testEquality ty1 ty2 = True + | otherwise = False diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Error.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Error.hs new file mode 100644 index 000000000..b3d719da0 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Error.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE Safe #-} + +-- | +-- Copyright: (c) 2011 National Institute of Aerospace / Galois, Inc. +-- +-- Custom functions to report error messages to users. +module Copilot.Compile.Bluespec.Error + ( impossible ) + where + +-- | Report an error due to a bug in Copilot. +impossible :: String -- ^ Name of the function in which the error was detected. + -> String -- ^ Name of the package in which the function is located. + -> a +impossible function package = + error $ "Impossible error in function " + ++ function ++ ", in package " ++ package + ++ ". Please file an issue at " + ++ "https://github.com/Copilot-Language/copilot/issues" + ++ " or email the maintainers at " diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs new file mode 100644 index 000000000..c4978aef5 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs @@ -0,0 +1,671 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Translate Copilot Core expressions and operators to Bluespec. +module Copilot.Compile.Bluespec.Expr + ( transExpr + , cIndexVector + , cLit + , constTy + , genVector + ) where + +-- External imports +import Data.Foldable (foldl') +import Data.String (IsString (..)) +import qualified Language.Bluespec.Classic.AST as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS + +-- Internal imports: Copilot +import Copilot.Core + +-- Internal imports +import Copilot.Compile.Bluespec.Error (impossible) +import Copilot.Compile.Bluespec.Name +import Copilot.Compile.Bluespec.Type + +-- | Translates a Copilot Core expression into a Bluespec expression. +transExpr :: Expr a -> BS.CExpr +transExpr (Const ty x) = constTy ty x + +transExpr (Local ty1 _ name e1 e2) = + let nameId = BS.mkId BS.NoPos $ fromString name + e1' = transExpr e1 + ty1' = transType ty1 + e2' = transExpr e2 in + BS.Cletrec + [ BS.CLValueSign + (BS.CDef nameId (BS.CQType [] ty1') [BS.CClause [] [] e1']) + [] + ] + e2' + +transExpr (Var _ n) = BS.CVar $ BS.mkId BS.NoPos $ fromString n + +transExpr (Drop _ amount sid) = + let accessVar = streamAccessorName sid + index = BS.LInt $ BS.ilDec $ fromIntegral amount in + BS.CApply (BS.CVar $ BS.mkId BS.NoPos $ fromString accessVar) + [BS.CLit $ BS.CLiteral BS.NoPos index] + +transExpr (ExternVar _ name _) = + let ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName in + BS.CSelect + (BS.CSelect + (BS.CVar ifcArgId) + (BS.mkId BS.NoPos $ fromString $ lowercaseName name)) + (BS.id_read BS.NoPos) + +transExpr (Label _ _ e) = transExpr e -- ignore label + +transExpr (Op1 op e) = transOp1 op (transExpr e) + +transExpr (Op2 op e1 e2) = transOp2 op (transExpr e1) (transExpr e2) + +transExpr (Op3 op e1 e2 e3) = + transOp3 op (transExpr e1) (transExpr e2) (transExpr e3) + +-- | Translates a Copilot unary operator and its argument into a Bluespec +-- function. +transOp1 :: Op1 a b -> BS.CExpr -> BS.CExpr +transOp1 op e = + case op of + Not -> app BS.idNot + Abs _ty -> app $ BS.mkId BS.NoPos "abs" + Sign ty -> transSign ty e + -- Bluespec's Arith class does not have a `recip` method corresponding to + -- Haskell's `recip` in the `Fractional` class, so we implement it + -- ourselves. + Recip ty -> BS.CApply + (BS.CVar (BS.idSlashAt BS.NoPos)) + [constNumTy ty 1, e] + BwNot _ty -> app $ BS.idInvertAt BS.NoPos + + Cast fromTy toTy -> transCast fromTy toTy e + GetField (Struct _) _ f -> BS.CSelect e $ BS.mkId BS.NoPos $ + fromString $ lowercaseName $ accessorName f + GetField _ _ _ -> impossible "transOp1" "copilot-bluespec" + + -- BDPI-supported operations + Sqrt ty -> appFP ty "sqrt" + Exp ty -> appFP ty "exp" + Log ty -> appFP ty "log" + Acos ty -> appFP ty "acos" + Asin ty -> appFP ty "asin" + Atan ty -> appFP ty "atan" + Cos ty -> appFP ty "cos" + Sin ty -> appFP ty "sin" + Tan ty -> appFP ty "tan" + Acosh ty -> appFP ty "acosh" + Asinh ty -> appFP ty "asinh" + Atanh ty -> appFP ty "atanh" + Cosh ty -> appFP ty "cosh" + Sinh ty -> appFP ty "sinh" + Tanh ty -> appFP ty "tanh" + Ceiling ty -> appFP ty "ceiling" + Floor ty -> appFP ty "floor" + where + app :: BS.Id -> BS.CExpr + app i = BS.CApply (BS.CVar i) [e] + + appFP :: forall t. Type t -> String -> BS.CExpr + appFP ty funPrefix = app $ fpFunId ty funPrefix + +-- | Translates a Copilot binary operator and its arguments into a Bluespec +-- function. +transOp2 :: Op2 a b c -> BS.CExpr -> BS.CExpr -> BS.CExpr +transOp2 op e1 e2 = + case op of + And -> app BS.idAnd + Or -> app $ BS.idOrAt BS.NoPos + Add _ty -> app BS.idPlus + Sub _ty -> app BS.idMinus + Mul _ty -> app $ BS.idStarAt BS.NoPos + Mod _ty -> app $ BS.idPercentAt BS.NoPos + Div _ty -> app $ BS.idSlashAt BS.NoPos + Fdiv _ty -> app $ BS.idSlashAt BS.NoPos + Eq _ -> app BS.idEqual + Ne _ -> app BS.idNotEqual + Le ty -> transLe ty e1 e2 + Ge ty -> transGe ty e1 e2 + Lt ty -> transLt ty e1 e2 + Gt ty -> transGt ty e1 e2 + BwAnd _ -> app $ BS.idBitAndAt BS.NoPos + BwOr _ -> app $ BS.idBitOrAt BS.NoPos + BwXor _ -> app $ BS.idCaretAt BS.NoPos + BwShiftL _ _ -> app $ BS.idLshAt BS.NoPos + BwShiftR _ _ -> app $ BS.idRshAt BS.NoPos + Index _ -> cIndexVector e1 e2 + UpdateField (Struct _) _ f -> + let field :: BS.FString + field = fromString $ lowercaseName $ accessorName f in + BS.CStructUpd e1 [(BS.mkId BS.NoPos field, e2)] + UpdateField _ _ _ -> impossible "transOp2" "copilot-bluespec" + + -- BDPI-supported operations + Pow ty -> appFP ty "pow" + Logb ty -> appFP ty "logb" + Atan2 ty -> appFP ty "atan2" + where + app :: BS.Id -> BS.CExpr + app i = BS.CApply (BS.CVar i) [e1, e2] + + appFP :: forall t. Type t -> String -> BS.CExpr + appFP ty funPrefix = app $ fpFunId ty funPrefix + +-- | Translates a Copilot ternary operator and its arguments into a Bluespec +-- function. +transOp3 :: Op3 a b c d -> BS.CExpr -> BS.CExpr -> BS.CExpr -> BS.CExpr +transOp3 op e1 e2 e3 = + case op of + Mux _ -> BS.Cif BS.NoPos e1 e2 e3 + UpdateArray _ -> cUpdateVector e1 e2 e3 + +-- | Translate @'Sign' e@ in Copilot Core into a Bluespec expression. +-- +-- @signum e@ is translated as: +-- +-- @ +-- if e > 0 then 1 else (if e < 0 then negate 1 else e) +-- @ +-- +-- That is: +-- +-- 1. If @e@ is positive, return @1@. +-- +-- 2. If @e@ is negative, return @-1@. +-- +-- 3. Otherwise, return @e@. This handles the case where @e@ is @0@ when the +-- type is an integral type. If the type is a floating-point type, it also +-- handles the cases where @e@ is @-0@ or @NaN@. +-- +-- This implementation is modeled after how GHC implements 'signum' +-- . +transSign :: Type a -> BS.CExpr -> BS.CExpr +transSign ty e = positiveCase $ negativeCase e + where + -- If @e@ is positive, return @1@, otherwise fall back to the argument. + -- + -- Produces the following code, where @@ is the argument to this + -- function: + -- @ + -- if e > 0 then 1 else + -- @ + positiveCase :: BS.CExpr -- ^ Value returned if @e@ is not positive. + -> BS.CExpr + positiveCase = + BS.Cif BS.NoPos (transGt ty e (constNumTy ty 0)) (constNumTy ty 1) + + -- If @e@ is negative, return @1@, otherwise fall back to the argument. + -- + -- Produces the following code, where @@ is the argument to this + -- function: + -- @ + -- if e < 0 then negate 1 else + -- @ + negativeCase :: BS.CExpr -- ^ Value returned if @e@ is not negative. + -> BS.CExpr + negativeCase = + BS.Cif BS.NoPos (transLt ty e (constNumTy ty 0)) (constNumTy ty (-1)) + +-- | Translate a Copilot @x < y@ expression into Bluespec. We will generate +-- different code depending on whether the arguments have a floating-point type +-- or not. +transLt :: Type a + -- ^ The type of the arguments. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transLt ty e1 e2 + | typeIsFloating ty + = transLtOrGtFP (BS.mkId BS.NoPos "LT") e1 e2 + | otherwise + = BS.CApply (BS.CVar (BS.idLtAt BS.NoPos)) [e1, e2] + +-- | Translate a Copilot @x > y@ expression into Bluespec. We will generate +-- different code depending on whether the arguments have a floating-point type +-- or not. +transGt :: Type a + -- ^ The type of the arguments. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transGt ty e1 e2 + | typeIsFloating ty + = transLtOrGtFP (BS.mkId BS.NoPos "GT") e1 e2 + | otherwise + = BS.CApply (BS.CVar (BS.idGtAt BS.NoPos)) [e1, e2] + +-- | Translate a Copilot @x <= y@ expression into Bluespec. We will generate +-- different code depending on whether the arguments have a floating-point type +-- or not. +transLe :: Type a + -- ^ The type of the arguments. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transLe ty e1 e2 + | typeIsFloating ty + = transLeOrGeFP (BS.mkId BS.NoPos "LT") e1 e2 + | otherwise + = BS.CApply (BS.CVar (BS.idLtEqAt BS.NoPos)) [e1, e2] + +-- | Translate a Copilot @x >= y@ expression into Bluespec. We will generate +-- different code depending on whether the arguments have a floating-point type +-- or not. +transGe :: Type a + -- ^ The type of the arguments. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transGe ty e1 e2 + | typeIsFloating ty + = transLeOrGeFP (BS.mkId BS.NoPos "GT") e1 e2 + | otherwise + = BS.CApply (BS.CVar (BS.idGtEqAt BS.NoPos)) [e1, e2] + +-- | Translate a Copilot floating-point comparison involving @<@ or @>@ into a +-- Bluespec expression. Specifically, @x < y@ is translated to: +-- +-- @ +-- compareFP x y == LT +-- @ +-- +-- @x > y@ is translated similarly, except that @GT@ is used instead of @LT@. +-- +-- See the comments on 'compareFPExpr' for why we translate floating-point +-- comparison operators this way. +transLtOrGtFP :: BS.Id + -- ^ A @Disorder@ label, which we check against the result of + -- calling @compareFP@. This should be either @LT@ or @GT@. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transLtOrGtFP disorderLabel e1 e2 = + BS.CApply + (BS.CVar BS.idEqual) + [compareFPExpr e1 e2, BS.CCon disorderLabel []] + +-- | Translate a Copilot floating-point comparison involving @<=@ or @>=@ into +-- a Bluespec expression. Specifically, @x <= y@ is translated to: +-- +-- @ +-- let _c = compareFP x y +-- in (_c == LT) || (_c == EQ) +-- @ +-- +-- @x >= y@ is translated similarly, except that @GT@ is used instead of @LT@. +-- +-- See the comments on 'compareFPExpr' for why we translate floating-point +-- comparison operators this way. +transLeOrGeFP :: BS.Id + -- ^ A @Disorder@ label, which we check against the result of + -- calling @compareFP@. This should be either @LT@ or @GT@. + -> BS.CExpr -> BS.CExpr -> BS.CExpr +transLeOrGeFP disorderLabel e1 e2 = + BS.Cletrec + [BS.CLValue c [BS.CClause [] [] (compareFPExpr e1 e2)] []] + (BS.CApply + (BS.CVar (BS.idOrAt BS.NoPos)) + [ BS.CApply + (BS.CVar BS.idEqual) + [BS.CVar c, BS.CCon disorderLabel []] + , BS.CApply + (BS.CVar BS.idEqual) + [BS.CVar c, BS.CCon (BS.mkId BS.NoPos "EQ") []] + ]) + where + c = BS.mkId BS.NoPos "_c" + +-- | Generate an expression of the form @compareFP x y@. This is used to power +-- the translations of the Copilot @<@, @<=@, @>@, and @>=@ floating-point +-- operators to Bluespec. +-- +-- Translating these operators using @compareFP@ is a somewhat curious design +-- choice, given that Bluespec already defines its own versions of these +-- operators. Unfortunately, we cannot directly use the Bluespec versions of +-- these operators, as they are defined in such a way that they will call +-- @error@ when one of the arguments is a NaN value. This would pose two +-- problems: +-- +-- 1. This would differ from the semantics of Copilot, where @x < y@ will return +-- @False@ (instead of erroring) when one of the arguments is NaN. (Similarly +-- for the other floating-point comparison operators.) +-- +-- 2. Moreover, if you have a Bluespec program that calls @x < y@, where the +-- value of @x@ or @y@ is derived from a register, then @bsc@ will always +-- fail to compile the code. This is because Bluespec must generate hardware +-- for all possible code paths in @<@, and because one of the code paths +-- calls @error@, this will cause compilation to result in an error. (See +-- https://github.com/B-Lang-org/bsc/discussions/711#discussioncomment-10003586 +-- for a more detailed explanation.) +-- +-- As such, we avoid using Bluespec's comparison operators and instead translate +-- Copilot's comparison operators to expressions derived from @compareFP@. +-- Unlike Bluespec's other comparison operators, calling @compareFP@ will never +-- result in an error. +compareFPExpr :: BS.CExpr -> BS.CExpr -> BS.CExpr +compareFPExpr e1 e2 = + BS.CApply + (BS.CVar (BS.mkId BS.NoPos "compareFP")) + [e1, e2] + +-- | Bluespec does not have a general-purpose casting operation, so we must +-- handle casts on a case-by-case basis. +transCast :: Type a -> Type b -> BS.CExpr -> BS.CExpr +transCast fromTy toTy = + case (fromTy, toTy) of + ----- + -- "safe" casts that cannot lose information + ----- + + (Bool, Bool) -> id + (Bool, Word8) -> upcastBool + (Bool, Word16) -> upcastBool + (Bool, Word32) -> upcastBool + (Bool, Word64) -> upcastBool + (Bool, Int8) -> upcastBool + (Bool, Int16) -> upcastBool + (Bool, Int32) -> upcastBool + (Bool, Int64) -> upcastBool + + (Int8, Int8) -> id + (Int8, Int16) -> upcast + (Int8, Int32) -> upcast + (Int8, Int64) -> upcast + (Int16, Int16) -> id + (Int16, Int32) -> upcast + (Int16, Int64) -> upcast + (Int32, Int32) -> id + (Int32, Int64) -> upcast + (Int64, Int64) -> id + + (Word8, Int16) -> unpackPackUpcast Word16 + (Word8, Int32) -> unpackPackUpcast Word32 + (Word8, Int64) -> unpackPackUpcast Word64 + (Word8, Word8) -> id + (Word8, Word16) -> upcast + (Word8, Word32) -> upcast + (Word8, Word64) -> upcast + (Word16, Int32) -> unpackPackUpcast Word32 + (Word16, Int64) -> unpackPackUpcast Word64 + (Word16, Word16) -> id + (Word16, Word32) -> upcast + (Word16, Word64) -> upcast + (Word32, Int64) -> unpackPackUpcast Word64 + (Word32, Word32) -> id + (Word32, Word64) -> upcast + (Word64, Word64) -> id + + ----- + -- "unsafe" casts, which may lose information + ----- + + -- unsigned truncations + (Word64, Word32) -> downcast + (Word64, Word16) -> downcast + (Word64, Word8) -> downcast + (Word32, Word16) -> downcast + (Word32, Word8) -> downcast + (Word16, Word8) -> downcast + + -- signed truncations + (Int64, Int32) -> downcast + (Int64, Int16) -> downcast + (Int64, Int8) -> downcast + (Int32, Int16) -> downcast + (Int32, Int8) -> downcast + (Int16, Int8) -> downcast + + -- signed integer to float + (Int64, Float) -> castIntegralToFloatingPoint + (Int32, Float) -> castIntegralToFloatingPoint + (Int16, Float) -> castIntegralToFloatingPoint + (Int8, Float) -> castIntegralToFloatingPoint + + -- unsigned integer to float + (Word64, Float) -> castIntegralToFloatingPoint + (Word32, Float) -> castIntegralToFloatingPoint + (Word16, Float) -> castIntegralToFloatingPoint + (Word8, Float) -> castIntegralToFloatingPoint + + -- signed integer to double + (Int64, Double) -> castIntegralToFloatingPoint + (Int32, Double) -> castIntegralToFloatingPoint + (Int16, Double) -> castIntegralToFloatingPoint + (Int8, Double) -> castIntegralToFloatingPoint + + -- unsigned integer to double + (Word64, Double) -> castIntegralToFloatingPoint + (Word32, Double) -> castIntegralToFloatingPoint + (Word16, Double) -> castIntegralToFloatingPoint + (Word8, Double) -> castIntegralToFloatingPoint + + -- unsigned to signed conversion + (Word64, Int64) -> unpackPack + (Word32, Int32) -> unpackPack + (Word16, Int16) -> unpackPack + (Word8, Int8) -> unpackPack + + -- signed to unsigned conversion + (Int64, Word64) -> unpackPack + (Int32, Word32) -> unpackPack + (Int16, Word16) -> unpackPack + (Int8, Word8) -> unpackPack + + _ -> impossible "transCast" "copilot-bluespec" + where + -- unpackPack :: (Bits fromTy n, Bits toTy n) => fromTy -> toTy + -- unpackPack e = (unpack (pack e)) :: toTy + -- + -- The most basic cast. Used when fromTy and toTy are both integral types + -- with the same number of bits. + unpackPack :: BS.CExpr -> BS.CExpr + unpackPack e = withTypeAnnotation toTy $ + BS.CApply + (BS.CVar BS.idUnpack) + [BS.CApply (BS.CVar BS.idPack) [e]] + + -- upcastBool :: (Add k 1 n, Bits toTy n) => Bool -> toTy + -- upcastBool b = (unpack (extend (pack b))) :: toTy + -- + -- Cast a Bool to a `Bits 1` value, extend it to `Bits n`, and then + -- convert it back to an integral type. + upcastBool :: BS.CExpr -> BS.CExpr + upcastBool e = withTypeAnnotation toTy $ + BS.CApply + (BS.CVar BS.idUnpack) + [BS.CApply extendExpr [BS.CApply (BS.CVar BS.idPack) [e]]] + + -- upcast :: (BitExtend m n x) => x m -> x n + -- upcast e = (extend e) :: ty + -- + -- Convert a narrower integral type to a wider integral type (e.g., + -- `UInt 8` to `UInt 64` or `Int 8` to `Int 64`). Note that the `extend` + -- operation is smart enough to choose between sign-extension and + -- zero-extension depending on whether `x m` (i.e., fromTy) is a signed + -- or unsigned type, respectively. + upcast :: BS.CExpr -> BS.CExpr + upcast e = withTypeAnnotation toTy $ BS.CApply extendExpr [e] + + -- downcast :: (BitExtend m n x) => x n -> x m + -- downcast e = (truncate e) :: ty + -- + -- Convert a wider integral type to a narrow integral type (e.g., + -- `UInt 64` to `UInt 8` or `Int 64` to `Int 8`) by truncating the most + -- significant bits. + downcast :: BS.CExpr -> BS.CExpr + downcast e = withTypeAnnotation toTy $ BS.CApply truncateExpr [e] + + -- Apply upcast followed by unpackPack. This requires supplying the type to + -- upcast to for type disambiguation purposes. + unpackPackUpcast :: Type a -> BS.CExpr -> BS.CExpr + unpackPackUpcast upcastTy e = unpackPack $ + withTypeAnnotation upcastTy $ BS.CApply extendExpr [e] + + -- castIntegralToFloatingPoint :: (FixedFloatCVT fromTy toTy) => fromTy toTy + -- castIntegralToFloatingPoint e = + -- ((vFixedToFloat e (0 :: UInt 64) Rnd_Nearest_Even).fst) :: tfl + -- + -- While FloatingPoint does have a Bits instance, we don't want to convert + -- an integral type to a FloatingPoint type using the Bits class, as the + -- bit representation of an integral value differs quite a bit from the bit + -- representation of a FloatingPoint value. Instead, we use the + -- special-purpose FixedFloatCVT class for this task. + castIntegralToFloatingPoint :: BS.CExpr -> BS.CExpr + castIntegralToFloatingPoint e = + withTypeAnnotation toTy $ + BS.CSelect + (BS.CApply + (BS.CVar (BS.mkId BS.NoPos "vFixedToFloat")) + [ e + , constNumTy Word64 0 + , fpRM + ]) + BS.idPrimFst + + extendExpr = BS.CVar $ BS.mkId BS.NoPos "extend" + truncateExpr = BS.CVar $ BS.mkId BS.NoPos "truncate" + +-- | Transform a Copilot Core literal, based on its value and type, into a +-- Bluespec expression. +constTy :: Type a -> a -> BS.CExpr +constTy ty = + case ty of + -- The treatment of scalar types is relatively straightforward. Note that + -- Bool is an enum, so we must construct it using a CCon rather than with + -- a CLit. + Bool -> \v -> BS.CCon (if v then BS.idTrue else BS.idFalse) [] + Int8 -> constInt ty . toInteger + Int16 -> constInt ty . toInteger + Int32 -> constInt ty . toInteger + Int64 -> constInt ty . toInteger + Word8 -> constInt ty . toInteger + Word16 -> constInt ty . toInteger + Word32 -> constInt ty . toInteger + Word64 -> constInt ty . toInteger + Float -> constFP ty . realToFrac + Double -> constFP ty + + -- Translating a Copilot array literal to a Bluespec Vector is somewhat + -- involved. Given a Copilot array {x_0, ..., x_(n-1)}, the + -- corresponding Bluespec Vector will look something like: + -- + -- let arr = update (... (update newVector 0 x_0)) (n-1) x_(n-1) + -- + -- We use the `update` function instead of the := syntax (e.g., + -- { array_temp[0] := x; array_temp[1] := y; ...}) so that we can construct + -- a Vector in a pure context. + Array ty' -> constVector ty' . arrayElems + + -- Converting a Copilot struct { field_0 = x_0, ..., field_(n-1) = x_(n-1) } + -- to a Bluespec struct is quite straightforward, given Bluespec's struct + -- initialization syntax. + Struct s -> \v -> + BS.CStruct + (Just True) + (BS.mkId BS.NoPos $ fromString $ uppercaseName $ typeName s) + (map (\(Value ty'' field@(Field val)) -> + ( BS.mkId BS.NoPos $ fromString + $ lowercaseName + $ fieldName field + , constTy ty'' val + )) + (toValues v)) + +-- | Transform a list of Copilot Core expressions of a given 'Type' into a +-- Bluespec @Vector@ expression. +constVector :: Type a -> [a] -> BS.CExpr +constVector ty = genVector (\_ -> constTy ty) + +-- | Transform a list of Copilot Core expressions into a Bluespec @Vector@ +-- expression. When invoking @genVector f es@, where @es@ has length @n@, the +-- resulting @Vector@ will consist of +-- @[f 0 (es !! 0), f 1 (es !! 1), ..., f (n-1) (es !! (n-1))]@. +genVector :: (Int -> a -> BS.CExpr) -> [a] -> BS.CExpr +genVector f vec = + snd $ + foldl' + (\(!i, !v) x -> + ( i+1 + , cUpdateVector + v + (cLit (BS.LInt (BS.ilDec (toInteger i)))) + (f i x) + )) + (0, BS.CVar (BS.mkId BS.NoPos "newVector")) + vec + +-- | Translate a literal number of type @ty@ into a Bluespec expression. +-- +-- PRE: The type of PRE is numeric (integer or floating-point), that +-- is, not boolean, struct or array. +constNumTy :: Type a -> Integer -> BS.CExpr +constNumTy ty = + case ty of + Float -> constFP ty . fromInteger + Double -> constFP ty . fromInteger + _ -> constInt ty + +-- | Translate a Copilot integer literal into a Bluespec expression. +constInt :: Type a -> Integer -> BS.CExpr +constInt ty i + -- Bluespec intentionally does not support negative literal syntax (e.g., + -- -42), so we must create negative integer literals using the `negate` + -- function. + | i >= 0 = withTypeAnnotation ty $ cLit $ BS.LInt $ BS.ilDec i + | otherwise = withTypeAnnotation ty $ + BS.CApply + (BS.CVar $ BS.idNegateAt BS.NoPos) + [cLit $ BS.LInt $ BS.ilDec $ negate i] + +-- | Translate a Copilot floating-point literal into a Bluespec expression. +constFP :: Type ty -> Double -> BS.CExpr +constFP ty d + -- Bluespec intentionally does not support negative literal syntax (e.g., + -- -42.5), so we must create negative floating-point literals using the + -- `negate` function. + | d >= 0 = withTypeAnnotation ty $ cLit $ BS.LReal d + | otherwise = withTypeAnnotation ty $ + BS.CApply + (BS.CVar $ BS.idNegateAt BS.NoPos) + [cLit $ BS.LReal $ negate d] + +-- | Create a Bluespec expression consisting of a literal. +cLit :: BS.Literal -> BS.CExpr +cLit = BS.CLit . BS.CLiteral BS.NoPos + +-- | Create a Bluespec expression that indexes into a @Vector@. +cIndexVector :: BS.CExpr -> BS.CExpr -> BS.CExpr +cIndexVector vec idx = + BS.CApply (BS.CVar (BS.mkId BS.NoPos "select")) [vec, idx] + +-- | Create a Bluespec expression that updates a @Vector@ element at a +-- particular index. +cUpdateVector :: BS.CExpr -> BS.CExpr -> BS.CExpr -> BS.CExpr +cUpdateVector vec idx newElem = + BS.CApply + (BS.CVar (BS.mkId BS.NoPos "update")) + [vec, idx, newElem] + +-- | Create a Bluespec identifier for a floating-point function that Bluespec +-- imports using BDPI. +fpFunId :: Type a -> String -> BS.Id +fpFunId ty funPrefix = + BS.mkId BS.NoPos $ fromString $ "bs_fp_" ++ funName + where + funName :: String + funName = + case ty of + Float -> funPrefix ++ "f" + Double -> funPrefix + _ -> impossible "fpFunId" "copilot-bluespec" + +-- | Explicitly annotate an expression with a type signature. This is necessary +-- to prevent expressions from having ambiguous types in certain situations. +withTypeAnnotation :: Type a -> BS.CExpr -> BS.CExpr +withTypeAnnotation ty e = e `BS.CHasType` BS.CQType [] (transType ty) + +-- | True if the type given is a floating point number. +typeIsFloating :: Type a -> Bool +typeIsFloating Float = True +typeIsFloating Double = True +typeIsFloating _ = False + +-- | We assume round-near-even throughout, but this variable can be changed if +-- needed. This matches the behavior of @fpRM@ in @copilot-theorem@'s +-- @Copilot.Theorem.What4.Translate@ module. +fpRM :: BS.CExpr +fpRM = BS.CCon (BS.mkId BS.NoPos "Rnd_Nearest_Even") [] diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/External.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/External.hs new file mode 100644 index 000000000..6d42105bb --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/External.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE ExistentialQuantification #-} + +-- | Represent information about externs needed in the generation of Bluespec +-- code for stream declarations and triggers. +module Copilot.Compile.Bluespec.External + ( External(..) + , gatherExts + ) where + +-- External imports +import Data.List (unionBy) + +-- Internal imports: Copilot +import Copilot.Core ( Expr (..), Stream (..), Trigger (..), Type, UExpr (..) ) + +-- | Representation of external variables. +data External = forall a. External + { extName :: String + , extType :: Type a + } + +-- | Collect all external variables from the streams and triggers. +-- +-- Although Copilot specifications can contain also properties and theorems, +-- the Bluespec backend currently only generates code for streams and triggers. +gatherExts :: [Stream] -> [Trigger] -> [External] +gatherExts streams triggers = streamsExts `extUnion` triggersExts + where + streamsExts = foldr (extUnion . streamExts) mempty streams + triggersExts = foldr (extUnion . triggerExts) mempty triggers + + streamExts :: Stream -> [External] + streamExts (Stream _ _ expr _) = exprExts expr + + triggerExts :: Trigger -> [External] + triggerExts (Trigger _ guard args) = guardExts `extUnion` argExts + where + guardExts = exprExts guard + argExts = concatMap uExprExts args + + uExprExts :: UExpr -> [External] + uExprExts (UExpr _ expr) = exprExts expr + + exprExts :: Expr a -> [External] + exprExts (Local _ _ _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (ExternVar ty name _) = [External name ty] + exprExts (Op1 _ e) = exprExts e + exprExts (Op2 _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (Op3 _ e1 e2 e3) = exprExts e1 `extUnion` exprExts e2 + `extUnion` exprExts e3 + exprExts (Label _ _ e) = exprExts e + exprExts _ = [] + + -- | Union over lists of External, we solely base the equality on the + -- extName's. + extUnion :: [External] -> [External] -> [External] + extUnion = unionBy (\a b -> extName a == extName b) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/FloatingPoint.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/FloatingPoint.hs new file mode 100644 index 000000000..a1683f1c1 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/FloatingPoint.hs @@ -0,0 +1,171 @@ +-- | Generate definitions that allow importing C implementations of +-- floating-point operations into Bluespec. +module Copilot.Compile.Bluespec.FloatingPoint + ( copilotBluespecFloatingPointBSV + , copilotBluespecFloatingPointC + ) where + +-- | The contents of the generated @BluespecFP.bsv@ file, which contains the +-- @import \"BDPI\"@ declarations needed to use imported C functions in +-- Bluespec. +copilotBluespecFloatingPointBSV :: String +copilotBluespecFloatingPointBSV = + unlines $ + [ "import FloatingPoint::*;" + , "" + ] ++ + concatMap + (\funName -> [importOp1 Float funName, importOp1 Double funName]) + unaryFloatOpNames ++ + concatMap + (\funName -> [importOp2 Float funName, importOp2 Double funName]) + [ "pow" + , "atan2" + , "logb" + ] + where + importOp1 :: FloatType -> String -> String + importOp1 ft funName = + "import \"BDPI\" function " ++ floatTypeName ft ++ " " ++ funNamePrefix + ++ funName ++ floatTypeSuffix ft ++ " (" ++ floatTypeName ft ++ " x);" + + importOp2 :: FloatType -> String -> String + importOp2 ft funName = + "import \"BDPI\" function " ++ floatTypeName ft ++ " " ++ funNamePrefix + ++ funName ++ floatTypeSuffix ft ++ " (" ++ floatTypeName ft ++ " x, " + ++ floatTypeName ft ++ " y);" + + floatTypeName :: FloatType -> String + floatTypeName Float = "Float" + floatTypeName Double = "Double" + +-- | The contents of the generated @bs_fp.c@ file, which contains the C wrapper +-- functions that Bluespec imports. +copilotBluespecFloatingPointC :: String +copilotBluespecFloatingPointC = + unlines $ + [ "#include " + , "" + , defineUnionType Float + , defineUnionType Double + ] ++ + concatMap + (\funName -> [defineOp1 Float funName, defineOp1 Double funName]) + unaryFloatOpNames ++ + concatMap + (\funName -> [defineOp2 Float funName, defineOp2 Double funName]) + [ "pow" + , "atan2" + ] ++ + -- There is no direct C counterpart to the `logb` function, so we implement + -- it in terms of `log`. + map + (\ft -> + defineOp2Skeleton ft "logb" $ + \_cFunName x y -> "log" ++ floatTypeSuffix ft ++ "(" ++ y ++ ") / log" + ++ floatTypeSuffix ft ++ "( " ++ x ++ ")") + [Float, Double] + where + defineUnionType :: FloatType -> String + defineUnionType ft = + unlines + [ "union " ++ unionTypeName ft ++ " {" + , " " ++ integerTypeName ft ++ " i;" + , " " ++ floatTypeName ft ++ " f;" + , "};" + ] + + defineOp1Skeleton :: + FloatType -> String -> (String -> String -> String) -> String + defineOp1Skeleton ft funName mkFloatOp = + let cFunName = funName ++ floatTypeSuffix ft in + unlines + [ integerTypeName ft ++ " " + ++ funNamePrefix ++ cFunName + ++ "(" ++ integerTypeName ft ++ " x) {" + , " union " ++ unionTypeName ft ++ " x_u;" + , " union " ++ unionTypeName ft ++ " r_u;" + , " x_u.i = x;" + , " r_u.f = " ++ mkFloatOp cFunName "x_u.f" ++ ";" + , " return r_u.i;" + , "}" + ] + + defineOp1 :: FloatType -> String -> String + defineOp1 ft funName = + defineOp1Skeleton ft funName $ + \cFunName x -> cFunName ++ "(" ++ x ++ ")" + + defineOp2Skeleton :: + FloatType -> String -> (String -> String -> String -> String) -> String + defineOp2Skeleton ft funName mkFloatOp = + let cFunName = funName ++ floatTypeSuffix ft in + unlines + [ integerTypeName ft ++ " " + ++ funNamePrefix ++ cFunName + ++ "(" ++ integerTypeName ft ++ " x, " ++ integerTypeName ft + ++ " y) {" + , " union " ++ unionTypeName ft ++ " x_u;" + , " union " ++ unionTypeName ft ++ " y_u;" + , " union " ++ unionTypeName ft ++ " r_u;" + , " x_u.i = x;" + , " y_u.i = y;" + , " r_u.f = " ++ mkFloatOp cFunName "x_u.f" "y_u.f" ++ ";" + , " return r_u.i;" + , "}" + ] + + defineOp2 :: FloatType -> String -> String + defineOp2 ft funName = + defineOp2Skeleton ft funName $ + \cFunName x y -> cFunName ++ "(" ++ x ++ ", " ++ y ++ ")" + + integerTypeName :: FloatType -> String + integerTypeName Float = "unsigned int" + integerTypeName Double = "unsigned long long" + + floatTypeName :: FloatType -> String + floatTypeName Float = "float" + floatTypeName Double = "double" + + unionTypeName :: FloatType -> String + unionTypeName Float = "ui_float" + unionTypeName Double = "ull_double" + +-- * Internals + +-- | Are we generating a function for a @float@ or @double@ operation? +data FloatType + = Float + | Double + +-- | The suffix to use in the generated function. +floatTypeSuffix :: FloatType -> String +floatTypeSuffix Float = "f" +floatTypeSuffix Double = "" + +-- | The prefix to use in the generated function. +funNamePrefix :: String +funNamePrefix = "bs_fp_" + +-- | The names of unary floating-point operations. +unaryFloatOpNames :: [String] +unaryFloatOpNames = + [ "exp" + , "log" + , "acos" + , "asin" + , "atan" + , "cos" + , "sin" + , "tan" + , "acosh" + , "asinh" + , "atanh" + , "cosh" + , "sinh" + , "tanh" + , "ceil" + , "floor" + , "sqrt" + ] diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs new file mode 100644 index 000000000..dbc714a3b --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs @@ -0,0 +1,95 @@ +-- | Naming of variables and functions in Bluespec. +module Copilot.Compile.Bluespec.Name + ( argNames + , generatorName + , guardName + , ifcArgName + , indexName + , lowercaseName + , specIfcName + , specIfcPkgName + , specTypesPkgName + , streamAccessorName + , streamElemName + , streamName + , uppercaseName + ) where + +-- External imports +import Data.Char (isLower, isUpper) + +-- External imports: Copilot +import Copilot.Core (Id) + +-- | Turn a specification name into the name of its module interface. +specIfcName :: String -> String +specIfcName prefix = uppercaseName (specIfcPkgName prefix) + +-- | Turn a specification name into the name of the package that declares its +-- module interface. Note that 'specIfcPkgName' is not necessarily the same name +-- as 'specIfcName', as the former does not need to begin with an uppercase +-- letter, but the latter does. +specIfcPkgName :: String -> String +specIfcPkgName prefix = prefix ++ "Ifc" + +-- | Turn a specification name into the name of the package that declares its +-- struct types. +specTypesPkgName :: String -> String +specTypesPkgName prefix = prefix ++ "Types" + +-- | Turn a stream id into a stream element name. +streamElemName :: Id -> Int -> String +streamElemName sId n = streamName sId ++ "_" ++ show n + +-- | The name of the variable of type @Ifc@. This is used to select +-- trigger functions and external variables. +ifcArgName :: String +ifcArgName = "ifc" + +-- | Create a Bluespec name that must start with an uppercase letter (e.g., a +-- struct or interface name). If the supplied name already begins with an +-- uppercase letter, this function returns the name unchanged. Otherwise, this +-- function prepends a @BS_@ prefix (short for \"Bluespec\") at the front. +uppercaseName :: String -> String +uppercaseName [] = [] +uppercaseName n@(c:_) + | isUpper c = n + | otherwise = "BS_" ++ n + +-- | Create a Bluespec name that must start with a lowercase letter (e.g., a +-- function or method name). If the supplied name already begins with a +-- lowercase letter, this function returns the name unchanged. Otherwise, this +-- function prepends a @bs_@ prefix (short for \"Bluespec\") at the front. +lowercaseName :: String -> String +lowercaseName [] = [] +lowercaseName n@(c:_) + | isLower c = n + | otherwise = "bs_" ++ n + +-- | Turn a stream id into a suitable Bluespec variable name. +streamName :: Id -> String +streamName sId = "s" ++ show sId + +-- | Turn a stream id into the global varname for indices. +indexName :: Id -> String +indexName sId = streamName sId ++ "_idx" + +-- | Turn a stream id into the name of its accessor function +streamAccessorName :: Id -> String +streamAccessorName sId = streamName sId ++ "_get" + +-- | Turn stream id into name of its generator function. +generatorName :: Id -> String +generatorName sId = streamName sId ++ "_gen" + +-- | Turn the name of a trigger into a guard generator. +guardName :: String -> String +guardName name = lowercaseName name ++ "_guard" + +-- | Turn a trigger name into a an trigger argument name. +argName :: String -> Int -> String +argName name n = lowercaseName name ++ "_arg" ++ show n + +-- | Enumerate all argument names based on trigger name. +argNames :: String -> [String] +argNames base = map (argName base) [0..] diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Representation.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Representation.hs new file mode 100644 index 000000000..d5bc44212 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Representation.hs @@ -0,0 +1,22 @@ +-- | Bluespec backend specific versions of selected `Copilot.Core` datatypes. +module Copilot.Compile.Bluespec.Representation + ( UniqueTrigger (..) + , UniqueTriggerId + , mkUniqueTriggers + ) + where + +import Copilot.Core ( Trigger (..) ) + +-- | Internal unique name for a trigger. +type UniqueTriggerId = String + +-- | A `Copilot.Core.Trigger` with an unique name. +data UniqueTrigger = UniqueTrigger UniqueTriggerId Trigger + +-- | Given a list of triggers, make their names unique. +mkUniqueTriggers :: [Trigger] -> [UniqueTrigger] +mkUniqueTriggers ts = zipWith mkUnique ts [0..] + where + mkUnique :: Trigger -> Integer -> UniqueTrigger + mkUnique t@(Trigger name _ _) n = UniqueTrigger (name ++ "_" ++ show n) t diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Settings.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Settings.hs new file mode 100644 index 000000000..47247b605 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Settings.hs @@ -0,0 +1,14 @@ +-- | Settings used by the code generator to customize the code. +module Copilot.Compile.Bluespec.Settings + ( BluespecSettings(..) + , mkDefaultBluespecSettings + ) where + +-- | Settings used to customize the code generated. +newtype BluespecSettings = BluespecSettings + { bluespecSettingsOutputDirectory :: FilePath + } + +-- | Default Bluespec settings. Output to the current directory. +mkDefaultBluespecSettings :: BluespecSettings +mkDefaultBluespecSettings = BluespecSettings "." diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Type.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Type.hs new file mode 100644 index 000000000..967b695b1 --- /dev/null +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Type.hs @@ -0,0 +1,95 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} + +-- | Translate Copilot Core expressions and operators to Bluespec. +module Copilot.Compile.Bluespec.Type + ( transType + , tVector + ) where + +-- External imports +import Data.String (IsString (..)) +import qualified Language.Bluespec.Classic.AST as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS +import qualified Language.Bluespec.Classic.AST.Builtin.Types as BS + +-- Internal imports: Copilot +import Copilot.Core + +-- Internal imports +import Copilot.Compile.Bluespec.Name + +-- | Translate a Copilot type to a Bluespec type. +transType :: Type a -> BS.CType +transType ty = case ty of + Bool -> BS.tBool + Int8 -> BS.tInt `BS.TAp` BS.cTNum 8 BS.NoPos + Int16 -> BS.tInt `BS.TAp` BS.cTNum 16 BS.NoPos + Int32 -> BS.tInt `BS.TAp` BS.cTNum 32 BS.NoPos + Int64 -> BS.tInt `BS.TAp` BS.cTNum 64 BS.NoPos + Word8 -> BS.tUInt `BS.TAp` BS.cTNum 8 BS.NoPos + Word16 -> BS.tUInt `BS.TAp` BS.cTNum 16 BS.NoPos + Word32 -> BS.tUInt `BS.TAp` BS.cTNum 32 BS.NoPos + Word64 -> BS.tUInt `BS.TAp` BS.cTNum 64 BS.NoPos + + Float -> BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.mkId BS.NoPos "Float" + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = BS.TItype 0 $ tFloatingPoint `BS.TAp` + BS.cTNum 8 BS.NoPos `BS.TAp` + BS.cTNum 23 BS.NoPos + } + Double -> BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.mkId BS.NoPos "Double" + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = BS.TItype 0 $ tFloatingPoint `BS.TAp` + BS.cTNum 11 BS.NoPos `BS.TAp` + BS.cTNum 52 BS.NoPos + } + Array ty' -> tVector `BS.TAp` BS.cTNum len BS.NoPos `BS.TAp` transType ty' + where + len = toInteger $ typeLength ty + Struct s -> BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.mkId BS.NoPos $ + fromString $ + uppercaseName $ + typeName s + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = + BS.TIstruct BS.SStruct $ + map (\(Value _tu field) -> + BS.mkId BS.NoPos $ + fromString $ + lowercaseName $ + fieldName field) + (toValues s) + } + +-- | The @Vector@ Bluespec data type. +tVector :: BS.CType +tVector = BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.idVector + , BS.tcon_kind = Just (BS.Kfun BS.KNum (BS.Kfun BS.KStar BS.KStar)) + , BS.tcon_sort = + BS.TIdata + { BS.tidata_cons = [BS.mkId BS.NoPos "V"] + , BS.tidata_enum = False + } + } + +-- | The @FloatingPoint@ Bluespec struct type. +tFloatingPoint :: BS.CType +tFloatingPoint = BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.mkId BS.NoPos "FloatingPoint" + , BS.tcon_kind = Just (BS.Kfun BS.KNum (BS.Kfun BS.KNum BS.KStar)) + , BS.tcon_sort = + BS.TIstruct BS.SStruct [ BS.mkId BS.NoPos "sign" + , BS.mkId BS.NoPos "exp" + , BS.mkId BS.NoPos "sfd" + ] + } diff --git a/copilot-bluespec/tests/Main.hs b/copilot-bluespec/tests/Main.hs new file mode 100644 index 000000000..928da7de3 --- /dev/null +++ b/copilot-bluespec/tests/Main.hs @@ -0,0 +1,18 @@ +-- | Test copilot-bluespec. +module Main where + +-- External imports +import Test.Framework (Test, defaultMain) + +-- Internal library modules being tested +import qualified Test.Copilot.Compile.Bluespec + +-- | Run all @copilot-bluespec@ tests. +main :: IO () +main = defaultMain tests + +-- | All @copilot-bluespec@ tests. +tests :: [Test.Framework.Test] +tests = + [ Test.Copilot.Compile.Bluespec.tests + ] diff --git a/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs new file mode 100644 index 000000000..ee99c6e75 --- /dev/null +++ b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs @@ -0,0 +1,1470 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -Wno-orphans #-} +-- | Test copilot-bluespec:Copilot.Compile.Bluespec. +module Test.Copilot.Compile.Bluespec + ( tests ) + where + +-- External imports +import Control.Arrow ((&&&)) +import Control.Exception (IOException, catch) +import Control.Monad (when) +import Data.AEq (AEq (..)) +import Data.Bits (Bits, complement) +import Data.Foldable (foldl') +import Data.List (intercalate, stripPrefix) +import Data.List.Extra (stripSuffix) +import Data.Type.Equality (testEquality) +import Data.Typeable (Proxy (..), (:~:) (Refl)) +import GHC.Float (castDoubleToWord64, + castFloatToWord32, + castWord32ToFloat, + castWord64ToDouble) +import GHC.TypeLits (KnownNat, natVal) +import Numeric.IEEE (infinity, nan) +import System.Directory (doesFileExist, + getTemporaryDirectory, + removeDirectoryRecursive, + setCurrentDirectory) +import System.IO (hPutStrLn, stderr) +import System.Posix.Temp (mkdtemp) +import System.Process (callProcess, readProcess) +import System.Random (Random) +import Test.Framework (Test, testGroup) +import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.QuickCheck (Arbitrary, Gen, Property, + arbitrary, choose, elements, + forAll, forAllBlind, frequency, + getPositive, ioProperty, once, + oneof, vectorOf, withMaxSuccess, + (.&&.)) +import Test.QuickCheck.Gen (chooseAny, chooseBoundedIntegral) +import Text.ParserCombinators.ReadPrec (minPrec) + +-- External imports: Copilot +import Copilot.Core hiding (Property) + +-- External imports +import Copilot.Compile.Bluespec (bluespecSettingsOutputDirectory, compile, + compileWith, mkDefaultBluespecSettings) + +-- Internal imports +import Test.Copilot.Compile.Bluespec.External (External (extName), gatherExts) + +-- * Constants + +-- | All tests for copilot-bluespec:Copilot.Compile.Bluespec. +tests :: Test.Framework.Test +tests = + testGroup "Copilot.Compile.Bluespec" + [ testGroup "Unit tests" + [ testProperty "Compile specification" testCompile + , testProperty "Compile specification in custom dir" testCompileCustomDir + , testProperty "Run specification" testRun + , testProperty "Run and compare results" testRunCompare + ] + , testGroup "Regression tests" + [ test14 + , test15 + ] + ] + +-- * Individual tests + +-- | Test compiling a spec. +testCompile :: Property +testCompile = once $ ioProperty $ do + tmpDir <- getTemporaryDirectory + setCurrentDirectory tmpDir + + testDir <- mkdtemp "CopilotTest" + setCurrentDirectory testDir + + compile "CopilotTest" spec + r <- compileBluespec "CopilotTest" [] + + setCurrentDirectory tmpDir + removeDirectoryRecursive testDir + + return r + + where + + spec = Spec streams observers triggers properties + + streams = [] + observers = [] + triggers = [ Trigger function guard args ] + properties = [] + + function = "func" + + guard = Const Bool True + + args = [] + +-- | Test compiling a spec in a custom directory. +testCompileCustomDir :: Property +testCompileCustomDir = once $ ioProperty $ do + tmpDir <- getTemporaryDirectory + setCurrentDirectory tmpDir + + testDir <- mkdtemp "CopilotTest" + + compileWith (mkDefaultBluespecSettings + { bluespecSettingsOutputDirectory = testDir }) + "CopilotTest" + spec + + setCurrentDirectory testDir + r <- compileBluespec "CopilotTest" [] + + setCurrentDirectory tmpDir + removeDirectoryRecursive testDir + + return r + + where + + spec = Spec streams observers triggers properties + + streams = [] + observers = [] + triggers = [ Trigger function guard args ] + properties = [] + + function = "nop" + + guard = Const Bool True + + args = [] + +-- | Test compiling a spec and running the resulting program. +-- +-- The actual behavior is ignored. +testRun :: Property +testRun = once $ ioProperty $ do + tmpDir <- getTemporaryDirectory + setCurrentDirectory tmpDir + + testDir <- mkdtemp "CopilotTest" + setCurrentDirectory testDir + + let bluespecProgram = unlines + [ "package Top where" + , "" + , "import CopilotTest" + , "import CopilotTestIfc" + , "import CopilotTestTypes" + , "" + , "copilotTestIfc :: Module CopilotTestIfc" + , "copilotTestIfc =" + , " module" + , " interface" + , " nop :: Action" + , " nop = return ()" + , "" + , "mkTop :: Module Empty" + , "mkTop = mkCopilotTest copilotTestIfc" + ] + + writeFile "Top.bs" bluespecProgram + + compile "CopilotTest" spec + r <- compileBluespec "Top" ["-g", "mkTop"] + + -- Compile a main program + r2 <- compileExecutable "mkTop" + callProcess "./mkTop" ["-m", "2"] + + setCurrentDirectory tmpDir + removeDirectoryRecursive testDir + + return $ r && r2 + + where + + spec = Spec streams observers triggers properties + + streams = [] + observers = [] + triggers = [ Trigger function guard args ] + properties = [] + + function = "nop" + + guard = Const Bool True + + args = [] + +-- | Test running compiled spec and comparing the results to the +-- expectation. +testRunCompare :: Property +testRunCompare = + -- It takes a pretty long time to run these tests, so we set the maximum + -- number of successful tests to 5 instead of the default (100) for the sake + -- of making the test suite complete in a more reasonable amount of time. + withMaxSuccess 5 $ + testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Int8 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Int16 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Int32 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Int64 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Word8 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Word16 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Word32 Bool)) + .&&. testRunCompare1 (arbitraryOpIntegralBool :: Gen (TestCase1 Word64 Bool)) + .&&. testRunCompare1 (arbitraryOpFloatingBool :: Gen (TestCase1 Float Bool)) + .&&. testRunCompare1 (arbitraryOpFloatingBool :: Gen (TestCase1 Double Bool)) + .&&. testRunCompare1 (arbitraryOpStruct :: Gen (TestCase1 MyStruct Int8)) + .&&. testRunCompare2 (arbitraryOp2Struct :: Gen (TestCase2 MyStruct Int8 MyStruct)) + .&&. testRunCompare2 (arbitraryArray2Num :: Gen (TestCase2 (Array 2 Int8) Word32 Int8)) + .&&. testRunCompare2 (arbitraryArray2Num :: Gen (TestCase2 (Array 2 Int16) Word32 Int16)) + .&&. testRunCompare3 (arbitraryArray3Num :: Gen (TestCase3 (Array 2 Int16) Word32 Int16 (Array 2 Int16))) + +-- * Regression tests + +-- | Regression tests for +-- https://github.com/Copilot-Language/copilot-bluespec/issues/14 which ensure +-- that @copilot-bluespec@ generates code for the @signum@ function that adheres +-- to Copilot's @signum@ semantics. +test14 :: Test.Framework.Test +test14 = + testGroup "#14" + [ testProperty "`signum @Int8` generates correct Bluespec code" $ + mkRegressionTest1 (Sign Int8) (fmap signum) + [-2, -1, 0, 1, 2] + , testProperty "`signum @Double` generates correct Bluespec code" $ + mkRegressionTest1 (Sign Double) (fmap signum) + [-nan, -infinity, -2, -1, -0.0, 0, 1, 2, infinity, nan] + ] + +-- | Regression tests for +-- https://github.com/Copilot-Language/copilot-bluespec/issues/15 which ensure +-- that @copilot-bluespec@ generates valid code for comparison operators (('<'), +-- ('<='), ('>'), and ('>=')) that are capable of handling NaN values. +test15 :: Test.Framework.Test +test15 = + testGroup "#15" + [ testProperty "Generates valid (<) code for NaNs" $ + mkRegressionTest2 (Lt Double) (zipWith (<)) vals + , testProperty "Generates valid (<=) code for NaNs" $ + mkRegressionTest2 (Le Double) (zipWith (<=)) vals + , testProperty "Generates valid (>) code for NaNs" $ + mkRegressionTest2 (Gt Double) (zipWith (>)) vals + , testProperty "Generates valid (>=) code for NaNs" $ + mkRegressionTest2 (Ge Double) (zipWith (>=)) vals + ] + where + vals :: [(Double, Double)] + vals = [(0, nan), (nan, 0)] + +-- | Test the behavior of a unary operation (an @'Op1' a b@ value) against its +-- expected behavior (as a Haskell function of type @[a] -> [b]@) using the +-- supplied inputs (of type @[a]@). This function is intended to be used to +-- construct regression tests. +mkRegressionTest1 :: (Typed a, Typed b, + DisplayableInBluespec b, ReadableFromBluespec b, AEq b) + => Op1 a b + -> ([a] -> [b]) + -> [a] + -> Property +mkRegressionTest1 op haskellFun vals = + let spec = alwaysTriggerArg1 (UExpr t2 appliedOp) + appliedOp = Op1 op (ExternVar t1 varName Nothing) + + len = length vals + inputs = filterOutUnusedExts + spec + [ (typeBluespec t1, + fmap (bluespecShow t1) vals, + varName) + ] + outputs = haskellFun vals in + + once $ + testRunCompareArg + inputs len outputs spec (typeBluespec t2) + + where + + t1 = typeOf + t2 = typeOf + + varName = "input" + +-- | Test the behavior of a binary operation (an @'Op2' a b c@ value) against +-- its expected behavior (as a Haskell function of type @[a] -> [b] -> [c]@) +-- using the supplied inputs (of type @[(a, b)]@). This function is intended to +-- be used to construct regression tests. +mkRegressionTest2 :: (Typed a, Typed b, Typed c, + DisplayableInBluespec c, ReadableFromBluespec c, AEq c) + => Op2 a b c + -> ([a] -> [b] -> [c]) + -> [(a, b)] + -> Property +mkRegressionTest2 op haskellFun vals = + let spec = alwaysTriggerArg1 (UExpr t3 appliedOp) + appliedOp = Op2 op (ExternVar t1 varName1 Nothing) + (ExternVar t2 varName2 Nothing) + + len = length vals + (vals1, vals2) = unzip vals + inputs = filterOutUnusedExts + spec + [ (typeBluespec t1, + fmap (bluespecShow t1) vals1, + varName1) + , (typeBluespec t2, + fmap (bluespecShow t2) vals2, + varName2) + ] + outputs = haskellFun vals1 vals2 in + + once $ + testRunCompareArg + inputs len outputs spec (typeBluespec t3) + + where + + t1 = typeOf + t2 = typeOf + t3 = typeOf + + varName1 = "input1" + varName2 = "input2" + +-- * Random generators + +-- ** Random function generators + +-- | Generator of functions that produce booleans. +arbitraryOpBool :: Typed a => Gen (Fun a Bool, [a] -> [Bool]) +arbitraryOpBool = + frequency + [ (5, arbitraryOp1Any) + , (5, funCompose1 <$> arbitraryOp1Bool <*> arbitraryOpBool) + , (2, funCompose2 <$> arbitraryOp2Bool <*> arbitraryOpBool <*> arbitraryOpBool) + , (1, funCompose2 <$> arbitraryOp2Eq <*> arbitraryOpBool <*> arbitraryOpBool) + ] + +-- | Generator of functions that take Bits and produce booleans. +arbitraryOpBoolBits :: (Typed a, Bits a) => Gen (Fun a Bool, [a] -> [Bool]) +arbitraryOpBoolBits = + frequency + [ (1, funCompose2 <$> arbitraryOp2Eq <*> arbitraryOpBits <*> arbitraryOpBits) + ] + +-- | Generator of functions that take Nums and produce booleans. +arbitaryOpBoolOrdEqNum :: (Typed a, Eq a, Ord a, Num a) + => Gen (Fun a Bool, [a] -> [Bool]) +arbitaryOpBoolOrdEqNum = + frequency + [ (1, funCompose2 <$> arbitraryOp2Eq <*> arbitraryOpNum <*> arbitraryOpNum) + , (1, funCompose2 <$> arbitraryOp2Ord <*> arbitraryOpNum <*> arbitraryOpNum) + ] + +-- | Generator of functions that take Floating point numbers and produce +-- booleans. +arbitraryOpBoolEqNumFloat :: (Typed t, Eq t, Num t, Floating t) + => Gen (Fun t Bool, [t] -> [Bool]) +arbitraryOpBoolEqNumFloat = + frequency + [ (1, funCompose2 <$> arbitraryOp2Eq <*> arbitraryOpNum <*> arbitraryOpFloat) + , (1, funCompose2 <$> arbitraryOp2Eq <*> arbitraryOpFloat <*> arbitraryOpNum) + ] + +-- | Generator of functions that take and produce Bits. +arbitraryOpBits :: (Bits t, Typed t) + => Gen (Fun t t, [t] -> [t]) +arbitraryOpBits = elements + [ (Op1 (BwNot typeOf), fmap complement) + ] + +-- | Generator of functions that take and produce Nums. +arbitraryOpNum :: (Typed t, Num t) => Gen (Fun t t, [t] -> [t]) +arbitraryOpNum = elements + [ (Op1 (Abs typeOf), fmap abs) + , (Op1 (Sign typeOf), fmap signum) + ] + +-- | Generator of functions that take an arrays and indicates and produce +-- elements from the array. +arbitraryArrayIx :: forall t n . (Typed t, KnownNat n, Num t) + => Gen ( Fun2 (Array n t) Word32 t + , [Array n t] -> [Word32] -> [t] + ) +arbitraryArrayIx = return + (Op2 (Index typeOf), zipWith (\x y -> arrayElems x !! fromIntegral y)) + +-- | Generator of functions that take arrays, indices, and values, and then +-- produce new arrays by updating the elements at the indices with the values. +arbitraryArrayUpdate :: forall t n . (Typed t, KnownNat n, Num t) + => Gen ( Fun3 (Array n t) Word32 t (Array n t) + , [Array n t] -> [Word32] -> [t] -> [Array n t] + ) +arbitraryArrayUpdate = return + (Op3 (UpdateArray typeOf), zipWith3 (\x y z -> array (updateAt y z (arrayElems x)))) + where + updateAt :: forall a. Word32 -> a -> [a] -> [a] + updateAt _ _ [] = [] + updateAt 0 x (_ : as) = x : as + updateAt n x (a : as) = a : updateAt (n-1) x as + +-- | Generator of functions that take structs produce fields of the struct. +arbitraryStructField :: Gen ( Fun MyStruct Int8 + , [MyStruct] -> [Int8] + ) +arbitraryStructField = elements + [ (Op1 (GetField typeOf typeOf myStruct1), fmap (unField . myStruct1)) + , (Op1 (GetField typeOf typeOf myStruct2), fmap (unField . myStruct2)) + ] + +-- | Generator of functions that take and produce structs, where the returned +-- structs have one field value updated. +arbitraryStructUpdate :: Gen ( Fun2 MyStruct Int8 MyStruct + , [MyStruct] -> [Int8] -> [MyStruct] + ) +arbitraryStructUpdate = elements + [ ( Op2 (UpdateField typeOf typeOf myStruct1) + , zipWith (\s i -> s { myStruct1 = Field i }) + ) + , ( Op2 (UpdateField typeOf typeOf myStruct2) + , zipWith (\s i -> s { myStruct2 = Field i }) + ) + ] + +-- | Generator of functions on Floating point numbers. +arbitraryOpFloat :: (Floating t, Typed t) => Gen (Fun t t, [t] -> [t]) +arbitraryOpFloat = elements + [ (Op1 (Recip typeOf), fmap recip) + , (Op1 (Exp typeOf), fmap exp) + , (Op1 (Sqrt typeOf), fmap sqrt) + , (Op1 (Log typeOf), fmap log) + , (Op1 (Sin typeOf), fmap sin) + , (Op1 (Tan typeOf), fmap tan) + , (Op1 (Cos typeOf), fmap cos) + , (Op1 (Asin typeOf), fmap asin) + , (Op1 (Atan typeOf), fmap atan) + , (Op1 (Acos typeOf), fmap acos) + , (Op1 (Sinh typeOf), fmap sinh) + , (Op1 (Tanh typeOf), fmap tanh) + , (Op1 (Cosh typeOf), fmap cosh) + , (Op1 (Asinh typeOf), fmap asinh) + , (Op1 (Atanh typeOf), fmap atanh) + , (Op1 (Acosh typeOf), fmap acosh) + ] + +-- | Generator of functions on that produce elements of any type. +arbitraryOp1Any :: forall a b + . (Arbitrary b, Typed a, Typed b) + => Gen (Fun a b, [a] -> [b]) +arbitraryOp1Any = oneof $ + [ (\v -> (\_ -> Const typeOf v, fmap (const v))) <$> arbitrary ] + ++ + rest + where + rest | Just Refl <- testEquality t1 t2 + = [return (id, id)] + | otherwise + = [] + + t1 :: Type a + t1 = typeOf + + t2 :: Type b + t2 = typeOf + +-- | Generator of functions on Booleans. +arbitraryOp1Bool :: Gen (Fun Bool Bool, [Bool] -> [Bool]) +arbitraryOp1Bool = elements + [ (Op1 Not, fmap not) + ] + +-- | Generator of binary functions on Booleans. +arbitraryOp2Bool :: Gen (Fun2 Bool Bool Bool, [Bool] -> [Bool] -> [Bool]) +arbitraryOp2Bool = elements + [ (Op2 And, zipWith (&&)) + , (Op2 Or, zipWith (||)) + ] + +-- | Generator of binary functions that take two Eq elements of the same type +-- and return a Bool. +arbitraryOp2Eq :: (Typed t, Eq t) + => Gen (Fun2 t t Bool, [t] -> [t] -> [Bool]) +arbitraryOp2Eq = elements + [ (Op2 (Eq typeOf), zipWith (==)) + , (Op2 (Ne typeOf), zipWith (/=)) + ] + +-- | Generator of binary functions that take two Ord elements of the same type +-- and return a Bool. +arbitraryOp2Ord :: (Typed t, Ord t) + => Gen (Fun2 t t Bool, [t] -> [t] -> [Bool]) +arbitraryOp2Ord = elements + [ (Op2 (Le typeOf), zipWith (<=)) + , (Op2 (Lt typeOf), zipWith (<)) + , (Op2 (Ge typeOf), zipWith (>=)) + , (Op2 (Gt typeOf), zipWith (>)) + ] + +-- ** Random data generators + +-- | Random array generator. +arbitraryArray :: forall n t . (KnownNat n, Random t) => Gen (Array n t) +arbitraryArray = array <$> vectorOf len chooseAny + where + len :: Int + len = fromIntegral $ natVal (Proxy :: Proxy n) + +-- | Random struct generator. +arbitraryStruct :: Gen MyStruct +arbitraryStruct = do + fld1 <- Field <$> gen + fld2 <- Field <$> gen + return $ MyStruct fld1 fld2 + where + gen :: Gen Int8 + gen = chooseBoundedIntegral (minBound, maxBound) + +-- ** Random test case generators + +-- | Generator for test cases on integral numbers that produce booleans. +arbitraryOpIntegralBool :: (Typed t, Bounded t, Integral t, Bits t) + => Gen (TestCase1 t Bool) +arbitraryOpIntegralBool = frequency + [ (5, mkTestCase1 + arbitraryOpBool + (chooseBoundedIntegral (minBound, maxBound))) + + , (2, mkTestCase1 + arbitraryOpBoolBits + (chooseBoundedIntegral (minBound, maxBound))) + + -- we need to use +1 because certain operations overflow the number + , (2, mkTestCase1 + arbitaryOpBoolOrdEqNum + (chooseBoundedIntegral (minBound + 1, maxBound))) + ] + +-- | Generator for test cases on floating-point numbers that produce booleans. +arbitraryOpFloatingBool :: (Random t, Typed t, Floating t, Eq t) + => Gen (TestCase1 t Bool) +arbitraryOpFloatingBool = oneof + [ mkTestCase1 arbitraryOpBoolEqNumFloat chooseAny + ] + +-- | Generator for test cases on Arrays selection producing values of the +-- array. +arbitraryArray2Num :: forall n a + . (KnownNat n, Num a, Random a, Typed a) + => Gen (TestCase2 (Array n a) Word32 a) +arbitraryArray2Num = oneof + [ mkTestCase2 arbitraryArrayIx arbitraryArray gen + ] + where + gen :: Gen Word32 + gen = choose (0, len - 1) + + len :: Word32 + len = fromIntegral $ natVal (Proxy :: Proxy n) + +-- | Generator for test cases on Arrays which update values of the array. +arbitraryArray3Num :: forall n a + . (KnownNat n, Num a, Random a, Typed a) + => Gen (TestCase3 (Array n a) Word32 a (Array n a)) +arbitraryArray3Num = oneof + [ mkTestCase3 arbitraryArrayUpdate arbitraryArray gen chooseAny + ] + where + gen :: Gen Word32 + gen = choose (0, len - 1) + + len :: Word32 + len = fromIntegral $ natVal (Proxy :: Proxy n) + +-- | Generator for test cases on structs that produce fields of the struct. +arbitraryOpStruct :: Gen (TestCase1 MyStruct Int8) +arbitraryOpStruct = oneof + [ mkTestCase1 + arbitraryStructField + arbitraryStruct + ] + +-- | Generator for test cases that take and produce structs, where the returned +-- structs have one field value updated. +arbitraryOp2Struct :: Gen (TestCase2 MyStruct Int8 MyStruct) +arbitraryOp2Struct = oneof + [ mkTestCase2 + arbitraryStructUpdate + arbitraryStruct + gen + ] + where + gen :: Gen Int8 + gen = chooseBoundedIntegral (minBound, maxBound) + +-- * Semantics + +-- ** Functions + +-- | Unary Copilot function. +type Fun a b = Expr a -> Expr b + +-- | Binary Copilot function. +type Fun2 a b c = Expr a -> Expr b -> Expr c + +-- | Ternary Copilot function. +type Fun3 a b c d = Expr a -> Expr b -> Expr c -> Expr d + +-- | Compose functions, paired with the Haskell functions that define their +-- idealized meaning. +funCompose1 :: (Fun b c, [b] -> [c]) + -> (Fun a b, [a] -> [b]) + -> (Fun a c, [a] -> [c]) +funCompose1 (f1, g1) (f2, g2) = (f1 . f2, g1 . g2) + +-- | Compose a binary function, with two functions, one for each argument. +funCompose2 :: (Fun2 b c d, [b] -> [c] -> [d]) + -> (Fun a b, [a] -> [b]) + -> (Fun a c, [a] -> [c]) + -> (Fun a d, [a] -> [d]) +funCompose2 (f1, g1) (f2, g2) (f3, g3) = + (uncurry f1 . (f2 &&& f3), uncurry g1 . (g2 &&& g3)) + +-- ** Test cases + +-- | Test case specification for specs with one input variable and one output. +data TestCase1 a b = TestCase1 + { wrapTC1Expr :: Spec + -- ^ Specification containing a trigger with an extern of type 'a'. + + , wrapTC1Fun :: [a] -> [b] + -- ^ Function expected to function in the same way as the Spec being + -- tested. + + , wrapTC1CopInp :: (Type a, String, Gen a) + -- ^ Input specification. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC1CopOut :: Type b + -- ^ The type of the output in Bluespec. + } + +-- | Test case specification for specs with two input variables and one output. +data TestCase2 a b c = TestCase2 + { wrapTC2Expr :: Spec + -- ^ Specification containing a trigger with an extern of type 'a' and a + -- trigger with an argument of type 'b'. + + , wrapTC2Fun :: [a] -> [b] -> [c] + -- ^ Function expected to function in the same way as the Spec being + -- tested. + + , wrapTC2CopInp1 :: (Type a, String, Gen a) + -- ^ Input specification for the first input. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC2CopInp2 :: (Type b, String, Gen b) + -- ^ Input specification for the second input. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC2CopOut :: Type c + -- ^ The type of the output in Bluespec. + } + +-- | Test case specification for specs with three input variables and one output. +data TestCase3 a b c d = TestCase3 + { wrapTC3Expr :: Spec + -- ^ Specification containing a trigger with an extern of type 'a', a + -- trigger with an argument of type 'b', and a trigger with an argument of + -- type 'c'. + + , wrapTC3Fun :: [a] -> [b] -> [c] -> [d] + -- ^ Function expected to function in the same way as the Spec being + -- tested. + + , wrapTC3CopInp1 :: (Type a, String, Gen a) + -- ^ Input specification for the first input. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC3CopInp2 :: (Type b, String, Gen b) + -- ^ Input specification for the second input. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC3CopInp3 :: (Type c, String, Gen c) + -- ^ Input specification for the second input. + -- + -- - The first element contains the type of the input in Bluespec. + -- + -- - The second contains the variable name in Bluespec. + -- + -- - The latter contains a randomized generator for values of the given + -- type. + + , wrapTC3CopOut :: Type d + -- ^ The type of the output in Bluespec. + } + +-- | Generate test cases for expressions that behave like unary functions. +mkTestCase1 :: (Typed a, Typed b) + => Gen (Fun a b, [a] -> [b]) + -> Gen a + -> Gen (TestCase1 a b) +mkTestCase1 genO gen = do + (copilotF, semF) <- genO + + let spec = alwaysTriggerArg1 (UExpr t2 appliedOp) + appliedOp = copilotF (ExternVar t1 varName Nothing) + + return $ + TestCase1 + { wrapTC1Expr = spec + , wrapTC1Fun = semF + , wrapTC1CopInp = ( t1, varName, gen ) + , wrapTC1CopOut = t2 + } + + where + + t1 = typeOf + t2 = typeOf + + varName = "input" + +-- | Generate test cases for expressions that behave like binary functions. +mkTestCase2 :: (Typed a, Typed b, Typed c) + => Gen (Fun2 a b c, [a] -> [b] -> [c]) + -> Gen a + -> Gen b + -> Gen (TestCase2 a b c) +mkTestCase2 genO genA genB = do + (copilotF, semF) <- genO + + let spec = alwaysTriggerArg1 (UExpr t3 appliedOp) + appliedOp = copilotF (ExternVar t1 varName1 Nothing) + (ExternVar t2 varName2 Nothing) + + return $ + TestCase2 + { wrapTC2Expr = spec + , wrapTC2Fun = semF + , wrapTC2CopInp1 = ( t1, varName1, genA ) + , wrapTC2CopInp2 = ( t2, varName2, genB ) + , wrapTC2CopOut = t3 + } + + where + + t1 = typeOf + t2 = typeOf + t3 = typeOf + + varName1 = "input1" + varName2 = "input2" + +-- | Generate test cases for expressions that behave like ternary functions. +mkTestCase3 :: (Typed a, Typed b, Typed c, Typed d) + => Gen (Fun3 a b c d, [a] -> [b] -> [c] -> [d]) + -> Gen a + -> Gen b + -> Gen c + -> Gen (TestCase3 a b c d) +mkTestCase3 genO genA genB genC = do + (copilotF, semF) <- genO + + let spec = alwaysTriggerArg1 (UExpr t4 appliedOp) + appliedOp = copilotF (ExternVar t1 varName1 Nothing) + (ExternVar t2 varName2 Nothing) + (ExternVar t3 varName3 Nothing) + + return $ + TestCase3 + { wrapTC3Expr = spec + , wrapTC3Fun = semF + , wrapTC3CopInp1 = ( t1, varName1, genA ) + , wrapTC3CopInp2 = ( t2, varName2, genB ) + , wrapTC3CopInp3 = ( t3, varName3, genC ) + , wrapTC3CopOut = t4 + } + + where + + t1 = typeOf + t2 = typeOf + t3 = typeOf + t4 = typeOf + + varName1 = "input1" + varName2 = "input2" + varName3 = "input3" + +-- | Test running a compiled Bluespec program and comparing the results. +testRunCompare1 :: (Show a, Typed a, + DisplayableInBluespec b, ReadableFromBluespec b, + AEq b, Typed b) + => Gen (TestCase1 a b) -> Property +testRunCompare1 ops = + forAllBlind ops $ \testCase -> + let (TestCase1 + { wrapTC1Expr = copilotSpec + , wrapTC1Fun = haskellFun + , wrapTC1CopInp = inputVar + , wrapTC1CopOut = outputType + }) = testCase + (bluespecTypeInput, bluespecInputName, gen) = inputVar + + in forAll (getPositive <$> arbitrary) $ \len -> + + forAll (vectorOf len gen) $ \vals -> do + + let inputs = filterOutUnusedExts + copilotSpec + [ (typeBluespec bluespecTypeInput, + fmap (bluespecShow bluespecTypeInput) vals, + bluespecInputName) + ] + outputs = haskellFun vals + + testRunCompareArg + inputs len outputs copilotSpec (typeBluespec outputType) + +-- | Test running a compiled Bluespec program and comparing the results. +testRunCompare2 :: (Show a1, Typed a1, Show a2, Typed a2, + DisplayableInBluespec b, ReadableFromBluespec b, + AEq b, Typed b) + => Gen (TestCase2 a1 a2 b) -> Property +testRunCompare2 ops = + forAllBlind ops $ \testCase -> + let (TestCase2 + { wrapTC2Expr = copilotSpec + , wrapTC2Fun = haskellFun + , wrapTC2CopInp1 = inputVar1 + , wrapTC2CopInp2 = inputVar2 + , wrapTC2CopOut = outputType + }) = + testCase + + (bluespecTypeInput1, bluespecInputName1, gen1) = inputVar1 + (bluespecTypeInput2, bluespecInputName2, gen2) = inputVar2 + + in forAll (getPositive <$> arbitrary) $ \len -> + forAll (vectorOf len gen1) $ \vals1 -> + forAll (vectorOf len gen2) $ \vals2 -> do + let inputs = filterOutUnusedExts + copilotSpec + [ (typeBluespec bluespecTypeInput1, + fmap (bluespecShow bluespecTypeInput1) vals1, + bluespecInputName1) + , (typeBluespec bluespecTypeInput2, + fmap (bluespecShow bluespecTypeInput2) vals2, + bluespecInputName2) + ] + outputs = haskellFun vals1 vals2 + + testRunCompareArg + inputs len outputs copilotSpec (typeBluespec outputType) + +-- | Test running a compiled Bluespec program and comparing the results. +testRunCompare3 :: (Show a1, Typed a1, Show a2, Typed a2, Show a3, Typed a3, + DisplayableInBluespec b, ReadableFromBluespec b, + AEq b, Typed b) + => Gen (TestCase3 a1 a2 a3 b) -> Property +testRunCompare3 ops = + forAllBlind ops $ \testCase -> + let (TestCase3 + { wrapTC3Expr = copilotSpec + , wrapTC3Fun = haskellFun + , wrapTC3CopInp1 = inputVar1 + , wrapTC3CopInp2 = inputVar2 + , wrapTC3CopInp3 = inputVar3 + , wrapTC3CopOut = outputType + }) = + testCase + + (bluespecTypeInput1, bluespecInputName1, gen1) = inputVar1 + (bluespecTypeInput2, bluespecInputName2, gen2) = inputVar2 + (bluespecTypeInput3, bluespecInputName3, gen3) = inputVar3 + + in forAll (getPositive <$> arbitrary) $ \len -> + forAll (vectorOf len gen1) $ \vals1 -> + forAll (vectorOf len gen2) $ \vals2 -> + forAll (vectorOf len gen3) $ \vals3 -> do + let inputs = filterOutUnusedExts + copilotSpec + [ (typeBluespec bluespecTypeInput1, + fmap (bluespecShow bluespecTypeInput1) vals1, + bluespecInputName1) + , (typeBluespec bluespecTypeInput2, + fmap (bluespecShow bluespecTypeInput2) vals2, + bluespecInputName2) + , (typeBluespec bluespecTypeInput3, + fmap (bluespecShow bluespecTypeInput3) vals3, + bluespecInputName3) + ] + outputs = haskellFun vals1 vals2 vals3 + + testRunCompareArg + inputs len outputs copilotSpec (typeBluespec outputType) + +-- | Test running a compiled Bluespec program and comparing the results, when +-- the program produces one output as an argument to a trigger that always +-- fires. +-- +-- PRE: all lists (second argument) of inputs have the length given as second +-- argument. +-- +-- PRE: the monitoring code this is linked against uses the function +-- @printBack@ with exactly one argument to pass the results. +testRunCompareArg :: forall b + . (DisplayableInBluespec b, ReadableFromBluespec b, AEq b) + => [(String, [String], String)] + -> Int + -> [b] + -> Spec + -> String + -> Property +testRunCompareArg inputs numInputs vals spec outputType = + ioProperty $ do + tmpDir <- getTemporaryDirectory + setCurrentDirectory tmpDir + + -- Operate in temporary directory + testDir <- mkdtemp "CopilotTest" + setCurrentDirectory testDir + + -- Produce wrapper program + let bluespecProgram = + testRunCompareArgBluespecProgram (Proxy :: Proxy b) inputs outputType + writeFile "Top.bs" bluespecProgram + + -- Produce copilot monitoring code + compile "CopilotTest" spec + r <- compileBluespec "Top" ["-g", "mkTop"] + + -- Compile main program + r2 <- compileExecutable "mkTop" + + -- Print result so far (for debugging purposes only) + {- + print r2 + print testDir + -} + + -- Run the program and compare the results. Note that we use (===) (using + -- the `AEq` class from the `ieee754` package) rather than (==) (using the + -- `Eq` class), as the former allows us to use exact equality comparisons + -- for floating-point types. This lets us ensure that we are handling NaN + -- and -0.0 values correctly. + out <- readProcess "./mkTop" ["-m", show (numInputs + 2)] "" + let outNums = readFromBluespec <$> lines out + comparison = outNums === vals + + -- Only clean up if the test succeeded; otherwise, we want to inspect it. + when comparison $ do + -- Remove temporary directory + setCurrentDirectory tmpDir + removeDirectoryRecursive testDir + + return $ r && r2 && comparison + +-- | Return a wrapper Bluespec program that runs for a number of clock cycles, +-- updating external stream registers on every cycle, running the monitors, and +-- publishing the results of any outputs. +testRunCompareArgBluespecProgram + :: DisplayableInBluespec b + => Proxy b + -> [(String, [String], String)] + -> String + -> String +testRunCompareArgBluespecProgram proxy inputs outputType = unlines $ + [ "package Top where" + , "" + , "import FloatingPoint" + , "import Vector" + , "" + , "import CopilotTest" + , "import CopilotTestIfc" + , "import CopilotTestTypes" + , "" + ] + ++ inputVecDecls ++ + [ "" + , "copilotTestIfc :: Module CopilotTestIfc" + , "copilotTestIfc =" + , " module" + ] + ++ inputRegs ++ + [ " i :: Reg (Bit 64) <- mkReg 0" + , " ready :: Reg Bool <- mkReg False" + , " interface" + , " printBack :: " ++ outputType ++ " -> Action" + , " printBack output = $display " ++ printBackDisplayArgs + , " when ready" + , "" + ] + ++ inputMethods ++ + [ "" + , " rules" + , " \"inputs\": when True ==> do" + ] + ++ inputUpdates ++ + [ " i := i + 1" + , " ready := True" + , "" + , "mkTop :: Module Empty" + , "mkTop = mkCopilotTest copilotTestIfc" + ] + where + printBackDisplayArgs :: String + printBackDisplayArgs = unwords (displayInBluespec proxy "output") + + inputVecDecls :: [String] + inputVecDecls = + concatMap + (\(bluespecType, _varName, _regName, inputVecName, inputVals) -> + [ inputVecName ++ " :: Vector " ++ show (length inputVals) ++ + " (" ++ bluespecType ++ ")" + , inputVecName ++ " = " ++ genVector inputVals + ]) + vars + + inputRegs :: [String] + inputRegs = + map + (\(bluespecType, _varName, regName, _inputVecName, _inputVals) -> + " " ++ regName ++ " :: Reg (" ++ bluespecType ++ ") <- mkRegU") + vars + + inputMethods :: [String] + inputMethods = + concatMap + (\(bluespecType, varName, regName, _inputVecName, _inputVals) -> + [ " " ++ varName ++ " :: Reg (" ++ bluespecType ++ ")" + , " " ++ varName ++ " = " ++ regName + ]) + vars + + inputUpdates :: [String] + inputUpdates = + map + (\(_bluespecType, _varName, regName, inputVecName, _inputVals) -> + " " ++ regName ++ " := select " ++ inputVecName ++ " i") + vars + + vars = map oneInput inputs + oneInput (bluespecTypeInput, inputVals, bluespecInputName) = + (bluespecTypeInput, inputVarName, inputRegVarName, inputVecVarName, + inputVals) + where + inputVarName = bluespecInputName + inputRegVarName = bluespecInputName ++ "Impl" + inputVecVarName = bluespecInputName ++ "Inputs" + +-- * Auxiliary functions + +-- ** Specs handling + +-- | Build a 'Spec' that triggers at every step, passing the given expression +-- as argument, and execution a function 'printBack'. +alwaysTriggerArg1 :: UExpr -> Spec +alwaysTriggerArg1 = triggerArg1 (Const Bool True) + + where + + -- | Build a 'Spec' that triggers based on a given boolean stream, passing + -- the given expression as argument, and execution a function 'printBack'. + triggerArg1 :: Expr Bool -> UExpr -> Spec + triggerArg1 guard expr = Spec streams observers triggers properties + + where + + streams = [] + observers = [] + properties = [] + + triggers = [ Trigger function guard args ] + function = "printBack" + args = [ expr ] + +-- | Filter out any elements in the input list (of type @[(a, b, String)]@) +-- whose first element (the name of an external stream) does not correspond to +-- the name of an external stream in the supplied 'Spec'. For example, a Copilot +-- source program may declare external streams, but if none of them are used in +-- the 'Spec', then the 'Spec' value itself will not contain any external stream +-- definitions. As a result, we want to ensure that the input list also does not +-- contain any external streams. +filterOutUnusedExts :: Spec -> [(a, b, String)] -> [(a, b, String)] +filterOutUnusedExts spec = filter (\(_, _, name) -> name `elem` extNames) + where + extNames = map extName $ gatherExts (specStreams spec) (specTriggers spec) + +-- ** Compilation of Bluespec programs + +-- | Compile a Bluespec file given its basename. +compileBluespec :: String -> [String] -> IO Bool +compileBluespec baseName extraArgs = do + result <- catch (do callProcess "bsc" $ extraArgs ++ + [ "-sim", "-quiet", "-u", + -- We suppress the G0023 warning, + -- which arises due to the `nop` + -- triggers defined above. See the + -- DESIGN.md document for more + -- details on what these warning + -- codes mean. + "-suppress-warnings", "G0023:S0080", + baseName ++ ".bs" ] + return True + ) + (\e -> do + hPutStrLn stderr $ + "copilot-bluespec: error: compileBluespec: " + ++ "cannot compile " ++ baseName ++ ".bs with bsc" + hPutStrLn stderr $ + "copilot-bluespec: exception: " ++ show (e :: IOException) + return False + ) + if result + then doesFileExist $ baseName ++ ".bo" + else return False + +-- | Compile a Bluespec file into an executable given its basename. +compileExecutable :: String -> IO Bool +compileExecutable topExe = do + result <- catch (do callProcess "bsc" [ "-sim", "-quiet" + , "-e", topExe + , "-o", topExe + , "bs_fp.c" + ] + return True + ) + (\e -> do + hPutStrLn stderr $ + "copilot-bluespec: error: compileExecutable: " + ++ "cannot compile " ++ topExe ++ " with bsc" + hPutStrLn stderr $ + "copilot-bluespec: exception: " + ++ show (e :: IOException) + return False + ) + if result + then doesFileExist topExe + else return False + +-- ** Interfacing between Haskell and Bluespec + +-- | Bluespec type used to store values of a given type. +typeBluespec :: Typed a => Type a -> String +typeBluespec Bool = "Bool" +typeBluespec Int8 = "Int 8" +typeBluespec Int16 = "Int 16" +typeBluespec Int32 = "Int 32" +typeBluespec Int64 = "Int 64" +typeBluespec Word8 = "UInt 8" +typeBluespec Word16 = "UInt 16" +typeBluespec Word32 = "UInt 32" +typeBluespec Word64 = "UInt 64" +typeBluespec Float = "Float" +typeBluespec Double = "Double" +typeBluespec t@(Array tE) = + "Vector " ++ show (typeLength t) ++ "(" ++ typeBluespec tE ++ ")" +typeBluespec (Struct s) = typeName s + +-- | Show a value of a given type in Bluespec. +bluespecShow :: Type a -> a -> String +bluespecShow Bool x = show x +bluespecShow Int8 x = bluespecShowIntegral x +bluespecShow Int16 x = bluespecShowIntegral x +bluespecShow Int32 x = bluespecShowIntegral x +bluespecShow Int64 x = bluespecShowIntegral x +bluespecShow Word8 x = bluespecShowIntegral x +bluespecShow Word16 x = bluespecShowIntegral x +bluespecShow Word32 x = bluespecShowIntegral x +bluespecShow Word64 x = bluespecShowIntegral x +bluespecShow Float x = bluespecShowRealFloat 32 castFloatToWord32 x +bluespecShow Double x = bluespecShowRealFloat 64 castDoubleToWord64 x +bluespecShow (Array tE) x = genVector $ map (bluespecShow tE) $ arrayElems x +bluespecShow (Struct s) x = + typeName s + ++ "{ " + ++ intercalate ";" + (map + (\(Value fldTy fld@(Field val)) -> + fieldName fld ++ " = " ++ bluespecShow fldTy val) + (toValues x)) + ++ "}" + +-- | Show a value of a integral type (e.g., 'Int8' or 'Word8'). +bluespecShowIntegral :: (Integral a, Num a, Ord a, Show a) => a -> String +bluespecShowIntegral x + | x >= 0 = show x + -- Bluespec Haskell doesn't have negative integer literals, so something like + -- "-42" won't parse. Instead, we must rely on Bluespec's `negate` function. + -- + -- We must be careful to negate an Integer literal rather than than a + -- fixed-precision literal. For instance, suppose we wanted to display + -- (-128 :: Int8). We wouldn't want to do this by displaying `negate 128`, + -- since 128 isn't a valid Int8 value—the maximum Int8 value is 127! + -- Instead, we want to display `fromInteger (negate 128)`, where 128 is an + -- Integer. This way, `negate` can turn `128` to `-128` without issues. + | otherwise = "fromInteger (negate " ++ show (abs (toInteger x)) ++ ")" + +-- | Show a value of a floating-point type (e.g., 'Float' or 'Double'). We make +-- sure to convert NaN and infinity values to the corresponding Bluespec +-- @FloatingPoint@ functions that construct these values. +bluespecShowRealFloat :: + (Num float, Ord float, RealFloat float, Show float, Show word) + => Int + -> (float -> word) + -> float + -> String +bluespecShowRealFloat floatSizeInBits castFloatToWord float + -- We want to ensure that NaN values are correctly translated to Bluespec, + -- bit by bit. We have two mechanisms to do so. On the Haskell side, we have + -- `ieee754`'s `nanWithPayload` function, and on the Bluespec side, we have + -- `FloatingPoint`'s `nanQuiet` function. Unfortunately, their arguments are + -- of different types, and it isn't quite clear how to express one function + -- in terms of the other. + -- + -- To avoid this problem, we take a more indirect approach: we first cast + -- the Haskell floating-point value to a word and then `unpack` the word to + -- a floating-point value on the Bluespec side. It's somewhat verbose, but + -- it gets the job done reliably. + | isNaN float + = "unpack (" ++ show (castFloatToWord float) ++ + " :: Bit " ++ show floatSizeInBits ++ ")" + + | isInfinite float + = "infinity " ++ show floatIsNeg + + | floatIsNeg + = "negate " ++ show (abs float) + + | otherwise + = show float + where + floatIsNeg = (float < 0) || isNegativeZero float + +-- | Given a list of elements as arguments, show a @Vector@ expression. For +-- example, @'genVector' [\"27\", \"42\"]@ will return +-- @\"updateVector (updateVector newVector 0 27) 1 42)\"@. +genVector :: [String] -> String +genVector vals = + snd $ + foldl' + (\(!i, !v) x -> + (i+1, "update (" ++ v ++ ") " ++ show i ++ " (" ++ x ++ ")")) + (0 :: Int, "newVector") + vals + +-- | Display a value of a given type in Bluespec using @$display@. +class DisplayableInBluespec a where + displayInBluespec :: + proxy a -- ^ The type of the value. + -> String -- ^ The name of the Bluespec variable. + -> [String] -- ^ All arguments that are passed to @$display@. + +-- | Most Bluespec types can be displayed using @fshow@. +fshowDisplay :: proxy a -> String -> [String] +fshowDisplay _ output = ["(fshow " ++ output ++ ")"] + +-- | We display floating-point numbers by converting them to an integer and +-- printing them in hexadecimal (using the @%x@ modifier). This somewhat unusual +-- choice is motivated by the fact that this output is easier to parse than the +-- @fshow@ output. +hexFloatDisplay :: proxy a -> String -> [String] +hexFloatDisplay _ output = ["\"%x\"", output] + +instance DisplayableInBluespec Bool where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Int8 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Int16 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Int32 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Int64 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Word8 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Word16 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Word32 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Word64 where + displayInBluespec = fshowDisplay + +instance DisplayableInBluespec Float where + displayInBluespec = hexFloatDisplay + +instance DisplayableInBluespec Double where + displayInBluespec = hexFloatDisplay + +instance DisplayableInBluespec a => DisplayableInBluespec (Array n a) where + displayInBluespec = fshowDisplay + +-- | @copilot-bluespec@–generated structs currently do not have @FShow@ +-- instances. (Perhaps they should: see +-- https://github.com/Copilot-Language/copilot-bluespec/issues/12) +-- In lieu of this, we manually define the same code that would be used in a +-- derived @FShow@ instance. +instance DisplayableInBluespec MyStruct where + displayInBluespec _ output = + [ "\"MyStruct { myStruct1 = %d ; myStruct2 = %d }\"" + , output ++ ".myStruct1" + , output ++ ".myStruct2" + ] + +-- | Read a value of a given type in Bluespec. +class ReadableFromBluespec a where + readFromBluespec :: String -> a + +instance ReadableFromBluespec Bool where + readFromBluespec = read + +instance ReadableFromBluespec Int8 where + readFromBluespec = read + +instance ReadableFromBluespec Int16 where + readFromBluespec = read + +instance ReadableFromBluespec Int32 where + readFromBluespec = read + +instance ReadableFromBluespec Int64 where + readFromBluespec = read + +instance ReadableFromBluespec Word8 where + readFromBluespec = read + +instance ReadableFromBluespec Word16 where + readFromBluespec = read + +instance ReadableFromBluespec Word32 where + readFromBluespec = read + +instance ReadableFromBluespec Word64 where + readFromBluespec = read + +-- We print out floating-point values in hexadecimal (see `hexFloatDisplay` +-- above), so we parse them accordingly here. + +instance ReadableFromBluespec Float where + readFromBluespec s = castWord32ToFloat $ read $ "0x" ++ s + +instance ReadableFromBluespec Double where + readFromBluespec s = castWord64ToDouble $ read $ "0x" ++ s + +instance (KnownNat n, ReadableFromBluespec a) => ReadableFromBluespec (Array n a) where + readFromBluespec s0 + | Just s1 <- stripPrefix "" s1 + = array $ map readFromBluespec $ words s2 + | otherwise + = error $ "Unexpected Vector fshow output: " ++ s0 + +instance ReadableFromBluespec MyStruct where + readFromBluespec str = + case readsEither (readsStruct minPrec) str of + Left err -> error err + Right ms -> ms + +-- | Attempt to read a value of type @a@. If successful, return 'Right' with +-- the read value. Otherwise, return @'Left' err@, where @err@ is an error +-- message describing what went wrong. +readsEither :: ReadS a -> String -> Either String a +readsEither readIt s = + case [ x | (x,"") <- readIt s ] of + [x] -> Right x + [] -> Left $ "readsEither: no parse: " ++ s + _ -> Left $ "readsEither: ambiguous parse: " ++ s + +-- ** Orphan instances for Arrays + +instance Eq a => Eq (Array n a) where + a1 == a2 = arrayElems a1 == arrayElems a2 + +instance AEq a => AEq (Array n a) where + a1 === a2 = arrayElems a1 === arrayElems a2 + +-- ** A simple struct definition for unit testing purposes + +data MyStruct = MyStruct + { myStruct1 :: Field "myStruct1" Int8 + , myStruct2 :: Field "myStruct2" Int8 + } + +-- | Like a derived @Eq@ instance, except that this looks through 'Field's. +instance Eq MyStruct where + MyStruct (Field f1a) (Field f2a) == MyStruct (Field f1b) (Field f2b) = + f1a == f1b && f2a == f2b +instance AEq MyStruct + +-- | Like a derived @Read@ instance, except that this adds 'Field' wrappers as +-- needed. +readsStruct :: Int -> ReadS MyStruct +readsStruct p = readParen (p > 10) $ \s -> do + ("MyStruct", s1) <- lex s + ("{", s2) <- lex s1 + ("myStruct1", s3) <- lex s2 + ("=", s4) <- lex s3 + (f1, s5) <- readsPrec 0 s4 + (";", s6) <- lex s5 + ("myStruct2", s7) <- lex s6 + ("=", s8) <- lex s7 + (f2, s9) <- readsPrec 0 s8 + ("}", s10) <- lex s9 + pure (MyStruct (Field f1) (Field f2), s10) + +instance Struct MyStruct where + typeName _ = "MyStruct" + toValues ms = [ Value Int8 (myStruct1 ms) + , Value Int8 (myStruct2 ms) + ] + +instance Typed MyStruct where + typeOf = Struct (MyStruct (Field 0) (Field 0)) + +-- | Unwrap a 'Field' to obtain the underlying value. +unField :: Field s t -> t +unField (Field val) = val diff --git a/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec/External.hs b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec/External.hs new file mode 100644 index 000000000..01b1038bb --- /dev/null +++ b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec/External.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE ExistentialQuantification #-} + +-- | This is a duplicate version of @Copilot.Compile.Bluespec.External@ that is +-- specific to the test suite. Ideally, we would move this into a common library +-- that is shared between both @copilot-bluespec@ and @copilot-c99@ so that we +-- can avoid this duplication. See +-- https://github.com/Copilot-Language/copilot-bluespec/issues/3. +-- +-- Represent information about externs needed in the generation of Bluespec +-- code for stream declarations and triggers. +module Test.Copilot.Compile.Bluespec.External + ( External(..) + , gatherExts + ) where + +-- External imports +import Data.List (unionBy) + +-- External imports: Copilot +import Copilot.Core ( Expr (..), Stream (..), Trigger (..), Type, UExpr (..) ) + +-- | Representation of external variables. +data External = forall a. External + { extName :: String + , extType :: Type a + } + +-- | Collect all external variables from the streams and triggers. +-- +-- Although Copilot specifications can contain also properties and theorems, +-- the Bluespec backend currently only generates code for streams and triggers. +gatherExts :: [Stream] -> [Trigger] -> [External] +gatherExts streams triggers = streamsExts `extUnion` triggersExts + where + streamsExts = foldr (extUnion . streamExts) mempty streams + triggersExts = foldr (extUnion . triggerExts) mempty triggers + + streamExts :: Stream -> [External] + streamExts (Stream _ _ expr _) = exprExts expr + + triggerExts :: Trigger -> [External] + triggerExts (Trigger _ guard args) = guardExts `extUnion` argExts + where + guardExts = exprExts guard + argExts = concatMap uExprExts args + + uExprExts :: UExpr -> [External] + uExprExts (UExpr _ expr) = exprExts expr + + exprExts :: Expr a -> [External] + exprExts (Local _ _ _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (ExternVar ty name _) = [External name ty] + exprExts (Op1 _ e) = exprExts e + exprExts (Op2 _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (Op3 _ e1 e2 e3) = exprExts e1 `extUnion` exprExts e2 + `extUnion` exprExts e3 + exprExts (Label _ _ e) = exprExts e + exprExts _ = [] + + -- | Union over lists of External, we solely base the equality on the + -- extName's. + extUnion :: [External] -> [External] -> [External] + extUnion = unionBy (\a b -> extName a == extName b) From 54674815ef416b4ad660199f31e4abec27632175 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Tue, 3 Jun 2025 11:12:14 -0400 Subject: [PATCH 191/483] copilot-bluespec: Adjust issue numbers in changelog. Refs #623. The `copilot-bluespec` package was originally developed in an external repo (https://github.com/Copilot-Language/copilot-bluespec), but there is interest in having all Copilot projects and libraries merged into the main https://github.com/Copilot-Language/copilot repo for ease of management. Now that the `copilot-bluespec` package lives in the `copilot` repo, this adjusts the issue numbers in the `copilot-bluespec` changelog to refer to the old repo so that they will not be confused with issues in the `copilot` repo. --- copilot-bluespec/CHANGELOG | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index e062468a0..d1ba77494 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,35 +1,41 @@ 2025-05-08 - * Version bump (4.4). (#43) + * Version bump (4.4). (copilot-bluespec#43) 2025-05-05 - * Version bump (4.3.1). (#41) - * Move Copilot.Compile.Bluespec.External out of shared directory. (#36) - * Allow building with filepath-1.5.*. (#39) + * Version bump (4.3.1). (copilot-bluespec#41) + * Move Copilot.Compile.Bluespec.External out of shared directory. + (copilot-bluespec#36) + * Allow building with filepath-1.5.*. (copilot-bluespec#39) 2025-03-10 - * Version bump (4.3). (#34) + * Version bump (4.3). (copilot-bluespec#34) 2025-01-20 - * Version bump (4.2). (#31) - * Implement missing floating-point operations. (#28) - * Allow using same trigger name in multiple declarations. (#30) + * Version bump (4.2). (copilot-bluespec#31) + * Implement missing floating-point operations. (copilot-bluespec#28) + * Allow using same trigger name in multiple declarations. + (copilot-bluespec#30) 2024-11-08 - * Version bump (4.1). (#25) + * Version bump (4.1). (copilot-bluespec#25) 2024-09-09 - * Version bump (4.0). (#23) - * Support translating Copilot array updates to Bluespec. (#19) + * Version bump (4.0). (copilot-bluespec#23) + * Support translating Copilot array updates to Bluespec. + (copilot-bluespec#19) 2024-08-03 - * Test GHC 9.4 through 9.8. (#20) + * Test GHC 9.4 through 9.8. (copilot-bluespec#20) 2024-07-11 - * Version bump (3.20). (#11) - * Support translating Copilot struct updates to Bluespec (#10). - * Fix a bug in the translation of the signum function. (#14) - * Fix a bug in the translation of floating-point comparisons. (#15) + * Version bump (3.20). (copilot-bluespec#11) + * Support translating Copilot struct updates to Bluespec. + (copilot-bluespec#10) + * Fix a bug in the translation of the signum function. + (copilot-bluespec#14) + * Fix a bug in the translation of floating-point comparisons. + (copilot-bluespec#15) 2024-03-08 - * Version bump (3.19). (#5) + * Version bump (3.19). (copilot-bluespec#5) * Create new library for Bluespec backend. From 7433ba32d87727f947357f78293bd8b900bba0f6 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 28 May 2025 15:15:36 -0400 Subject: [PATCH 192/483] copilot-bluespec: Document changes in CHANGELOG. Refs #623. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index d1ba77494..10f4486ec 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-18 + * Include `copilot-bluespec` in mainline `copilot` repo. (#623) + 2025-05-08 * Version bump (4.4). (copilot-bluespec#43) From fd769d4645f8c8e5cf6e457b719fc5fa7f93979e Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 20 Jun 2025 10:44:36 -0400 Subject: [PATCH 193/483] Explicitly list .cabal files in cabal.project. Refs #622. The `copilot-verifier` package was originally developed in an external repo (https://github.com/Copilot-Language/copilot-verifier), but there is interest in having all Copilot projects and libraries merged into the main https://github.com/Copilot-Language/copilot repo for ease of management. For now, we wish not to add `copilot-verifier` to the `cabal.project` file, as its version bounds are tighter than the range of GHC versions that `copilot` currently supports. As such, this commit explicitly lists the `.cabal` files of packages that we do want to include in the `cabal.project`, i.e., all of the `.cabal` files except for `copilot-verifier.cabal`. --- cabal.project | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index 00dfed318..812c95ba6 100644 --- a/cabal.project +++ b/cabal.project @@ -1,2 +1,10 @@ packages: - */*.cabal + copilot-bluespec/copilot-bluespec.cabal + copilot-c99/copilot-c99.cabal + copilot-core/copilot-core.cabal + copilot-interpreter/copilot-interpreter.cabal + copilot-language/copilot-language.cabal + copilot-libraries/copilot-libraries.cabal + copilot-prettyprinter/copilot-prettyprinter.cabal + copilot-theorem/copilot-theorem.cabal + copilot/copilot.cabal From c8f422175ce480bc6655c0bb6207d122b42ccf71 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 20 Jun 2025 10:03:13 -0400 Subject: [PATCH 194/483] copilot-verifier: Include in mainline copilot repo. Refs #622. The `copilot-verifier` package was originally developed in an external repo (https://github.com/Copilot-Language/copilot-verifier), but there is interest in having all Copilot projects and libraries merged into the main https://github.com/Copilot-Language/copilot repo for ease of management. This commit includes the last state of the old `copilot-verified` repo into the main Copilot repo as a new subdirectory, disregarding the commit history. --- copilot-verifier/CHANGELOG | 40 + copilot-verifier/LICENSE | 30 + copilot-verifier/README.md | 351 ++++ copilot-verifier/copilot-verifier.cabal | 152 ++ .../examples/Copilot/Verifier/Examples.hs | 97 + .../Examples/ShouldFail/Partial/AbsIntMin.hs | 28 + .../ShouldFail/Partial/AddSignedWrap.hs | 27 + .../Examples/ShouldFail/Partial/DivByZero.hs | 27 + .../ShouldFail/Partial/IndexOutOfBounds.hs | 31 + .../Examples/ShouldFail/Partial/ModByZero.hs | 27 + .../ShouldFail/Partial/MulSignedWrap.hs | 31 + .../ShouldFail/Partial/ShiftLTooLarge.hs | 32 + .../ShouldFail/Partial/ShiftRTooLarge.hs | 32 + .../ShouldFail/Partial/SubSignedWrap.hs | 27 + .../Verifier/Examples/ShouldPass/Arith.hs | 52 + .../Verifier/Examples/ShouldPass/Array.hs | 40 + .../Verifier/Examples/ShouldPass/ArrayGen.hs | 21 + .../Examples/ShouldPass/ArrayOfStructs.hs | 29 + .../ShouldPass/ArrayTriggerArgument.hs | 23 + .../Verifier/Examples/ShouldPass/Clock.hs | 46 + .../Verifier/Examples/ShouldPass/Counter.hs | 56 + .../Verifier/Examples/ShouldPass/Engine.hs | 44 + .../Examples/ShouldPass/FPNegation.hs | 62 + .../Verifier/Examples/ShouldPass/FPOps.hs | 88 + .../Verifier/Examples/ShouldPass/Heater.hs | 60 + .../Verifier/Examples/ShouldPass/IntOps.hs | 64 + .../Examples/ShouldPass/Partial/AbsIntMin.hs | 19 + .../ShouldPass/Partial/AddSignedWrap.hs | 19 + .../Examples/ShouldPass/Partial/DivByZero.hs | 19 + .../ShouldPass/Partial/IndexOutOfBounds.hs | 19 + .../Examples/ShouldPass/Partial/ModByZero.hs | 19 + .../ShouldPass/Partial/MulSignedWrap.hs | 19 + .../ShouldPass/Partial/ShiftLTooLarge.hs | 19 + .../ShouldPass/Partial/ShiftRTooLarge.hs | 19 + .../ShouldPass/Partial/SubSignedWrap.hs | 19 + .../Verifier/Examples/ShouldPass/Structs.hs | 92 + .../Examples/ShouldPass/UpdateArray.hs | 32 + .../Examples/ShouldPass/UpdateStruct.hs | 62 + .../Verifier/Examples/ShouldPass/Voting.hs | 64 + .../Verifier/Examples/ShouldPass/WCV.hs | 189 ++ copilot-verifier/exe/VerifyExamples.hs | 53 + copilot-verifier/src/Copilot/Verifier.hs | 1607 +++++++++++++++++ .../src/Copilot/Verifier/FloatMode.hs | 56 + copilot-verifier/src/Copilot/Verifier/Log.hs | 834 +++++++++ .../src/Copilot/Verifier/Solver.hs | 25 + copilot-verifier/test/Test.hs | 32 + 46 files changed, 4734 insertions(+) create mode 100644 copilot-verifier/CHANGELOG create mode 100644 copilot-verifier/LICENSE create mode 100644 copilot-verifier/README.md create mode 100644 copilot-verifier/copilot-verifier.cabal create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AbsIntMin.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AddSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/DivByZero.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/IndexOutOfBounds.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ModByZero.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/MulSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftLTooLarge.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftRTooLarge.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/SubSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Arith.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Array.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayGen.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayOfStructs.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayTriggerArgument.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Clock.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Counter.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Engine.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPNegation.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPOps.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Heater.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/IntOps.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AbsIntMin.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AddSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/DivByZero.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/IndexOutOfBounds.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ModByZero.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/MulSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftLTooLarge.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftRTooLarge.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/SubSignedWrap.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Structs.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateArray.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateStruct.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Voting.hs create mode 100644 copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/WCV.hs create mode 100644 copilot-verifier/exe/VerifyExamples.hs create mode 100644 copilot-verifier/src/Copilot/Verifier.hs create mode 100644 copilot-verifier/src/Copilot/Verifier/FloatMode.hs create mode 100644 copilot-verifier/src/Copilot/Verifier/Log.hs create mode 100644 copilot-verifier/src/Copilot/Verifier/Solver.hs create mode 100644 copilot-verifier/test/Test.hs diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG new file mode 100644 index 000000000..2923ff5ed --- /dev/null +++ b/copilot-verifier/CHANGELOG @@ -0,0 +1,40 @@ +2025-05-08 + * Version bump (4.4). (#85) + +2025-03-10 + * Version bump (4.3). (#82) + * Add `smtSolver` option to `VerifierOptions`. (#78) + * Add `smtFloatMode` option to `VerifierOptions`. (#79) + +2025-01-20 + * Version bump (4.2). (#76) + * Reject specs that use multiple triggers with the same name. (#74) + +2024-11-08 + * Version bump (4.1). (#72) + +2024-09-09 + * Version bump (4.0). (#69) + * Support verifying programs that use array updates. (#63) + * Support verifying programs that use struct updates. (#57) + +2024-08-03 + * Support building with `crucible-llvm-0.7` and `crux-llvm-0.9`. (#64) + * Support GHC 9.4 through 9.8. (#65) + +2024-07-30 + * When using `Noisy` verbosity, always log proof goals related to the + core correspondence proof, even if the goals are trivial. (#51) + * When using `Noisy` verbosity, log more information about which proof + goals arise before or after calling the `step()` function. (#52) + +2024-07-11 + * Version bump (3.20). (#58) + +2024-03-08 + * Version bump (3.19). (#53) + * Provide more detailed feedback upon a successful run of the verifier. + * Make the examples build with Copilot 3.19. (#53) + +2024-02-06 + * Initial release of `copilot-verifier`. diff --git a/copilot-verifier/LICENSE b/copilot-verifier/LICENSE new file mode 100644 index 000000000..7192c3586 --- /dev/null +++ b/copilot-verifier/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2021-2024 Galois Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Galois, Inc. nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/copilot-verifier/README.md b/copilot-verifier/README.md new file mode 100644 index 000000000..85e28d9f5 --- /dev/null +++ b/copilot-verifier/README.md @@ -0,0 +1,351 @@ +[![Build Status](https://github.com/Copilot-Language/copilot-verifier/workflows/copilot-verifier/badge.svg)](https://github.com/Copilot-Language/copilot-verifier/actions?query=workflow%3Acopilot-verifier) +[![Version on Hackage](https://img.shields.io/hackage/v/copilot-verifier.svg)](https://hackage.haskell.org/package/copilot-verifier) + +# Copilot Verifier + +Copilot Verifier is an add-on to the [Copilot Stream +DSL](https://copilot-language.github.io) for verifying the correctness of C +code generated by the `copilot-c99` package. + +The main idea of the verifier is to use the +[Crucible symbolic simulator](https://github.com/galoisinc/crucible) +to interpret the semantics of the generated C program and +and to produce verification conditions sufficient to guarantee +that the meaning of the generated program corresponds in a precise +way to the meaning of the original stream specification. The generated +verification conditions are then dispatched to SMT solvers to +be automatically solved. We will have more to say about exactly +what is meant by this correspondence below. + +Copilot Verifier is described in the ICFP 2023 paper [_Trustworthy Runtime +Verification via Bisimulation (Experience +Report)_](https://dl.acm.org/doi/abs/10.1145/3607841). + +## Building + +To build the verifier from source, first make sure you have met the following +prerequisites: + +* Ensure that you have the `cabal` and `ghc` executables in your `PATH`. If you + don't already have them, we recommend using `ghcup` to install them: + https://www.haskell.org/ghcup/. We recommend `Cabal` 3.10 or newer, and one of + GHC 9.4, 9.6, or 9.8. + +* Ensure that you have the `clang` and `llvm-link` utilities from LLVM in your + `PATH`. Currently, LLVM versions up to 16 are supported. LLVM binaries are + available at https://github.com/llvm/llvm-project/releases. + +* Ensure that you have the `z3` SMT solver in your `PATH`. `z3` binaries are + available at https://github.com/Z3Prover/z3/releases. + + Alternatively, the verifier can be configured to use one of the following + SMT solvers instead: + + * `cvc4`: https://cvc4.github.io/downloads.html + * `cvc5`: https://github.com/cvc5/cvc5/releases/ + * `yices`: https://yices.csl.sri.com + +Then, clone the repo and run: + +``` +$ git submodule update --init +$ cabal test copilot-verifier +``` + +This will clone the repository, build the verifier, and run the associated test +suite. If you have performed all of the steps above correctly, the test suite +should pass. + +We also provide a [Dockerfile](Dockerfile) which automates the process of +installing Copilot and the verifier. The Dockerfile can be built and run using +the following commands: + +``` +$ docker build -t . +$ docker run -it +``` + +Where `` is a unique name for the Docker image. + +## Using the Copilot Verifier + +The main interface to the verifier is the `Copilot.Verifier.verify` +function, which takes some options to drive the code generation +process and a Copilot `Spec` object. It will invoke the Copilot +code generator to obtain C sources, compile the C sources into +LLVM bitcode using the `clang` compiler front-end, then +parse and interpret the bitcode using `crucible`. After generating +verification conditions, it will dispatch them to an SMT solver, +and the result of those queries will be presented to the user. + +There are a number of examples (based on similar examples +from the main Copilot repository) demonstrating how to +incorporate the verifier into a Copilot program. +See the `copilot-verifier/examples` subdirectory of this repository. + +## Details of the Verification + +### Synopsis of Copilot semantics + +The Copilot DSL represents a certain nicely-behaved +class of discrete-time stream programs. Each `Stream` +in Copilot denotes an infinite stream of values; one may +just as well think that `Stream a` represents a pure mathematical +function `ℕ → a` from natural numbers to values of type `a`. +See the +[Copilot manual](https://ntrs.nasa.gov/api/citations/20200003164/downloads/20200003164.pdf) +for more details of the Copilot language itself and its semantics. + +One of the central design considerations for Copilot is that is should +be possible to implement stream programs using a fixed, finite amount +of storage. As a result, Copilot will only accept stream programs +that access a bounded amount of their input streams (including any +recursive stream references). This allows an +implementation strategy where the generated C code can use fixed-size +ring buffers to store a limited number of previous stream values. + +The execution model for the generated programs is that the program +starts in a state corresponding to stream values at time `t = 0`; +"external" stream input values are placed in distinguished global +variables by the calling environment, which then executes a `step()` +function to move to the next time step. The `step()` function captures +the values of these external inputs and does whatever computation is +necessary to update its internal state from time `t=n` to time +`t=n+1`. In addition, it will call any defined "trigger" functions +if the stream state at that time step satisfies the defined guard condition. +In short, the generated C program steps one moment at a time through +the stream program, consuming a sequence of input values provided by +a cooperating environment and calling handler functions whenever +states of interest occur. + +### The Desired Correspondence + +What does it mean, then, for a generated C program in this style +to correctly implement a given stream program? The intuition +is basically that after `n` calls to the `step` function, +the state of the ring-buffers of the C program should correctly +compute the value of the corresponding stream expression +evaluated at index `n`, assuming the C program has been fed +inputs corresponding to the first `n` values of the external stream +inputs. Moreover, the trigger functions should be called from +the `step` function exactly at the time values when the stream expressions +evaluate to true. + +The notion of correspondence for the values flowing in streams is +relatively straightforward: these values consist of fixed-width +machine integers, floating-point values, structs and fixed-length +arrays. For each, the appropriate notion of equality is fairly clear. + +Both the original `Stream` program and the generated C program +can be viewed straightforwardly as a transition system, and under +this view, the correspondence we want to establish is a bisimulation +between the states of the high-level stream program and the low-level +C program. The proof method for bisimulation requires us to provide +a "correspondence" relation between the program states, and then prove +three things about this relation: + +1. that the initial states of the programs are in the relation; +2. if we assume two arbitrary program states begin in the relation +and each takes a single transition (consuming corresponding inputs), +the resulting states are back in the relation; +3. that any observable +actions taken by one program are exactly mirrored by the other. + +On the high-level side of the bisimulation, the program +state is essentially just the value of the current time step `n`, +whereas on the C side it consists of the regions of global memory that +contain the ring-buffers and their current indices. The transition +relation for the high-level program just increments the time value by +one, and the transition for the C program is defined by the action +of the generated `step()` function. + +Suppose `s` is one of the stream definitions in the original Copilot +program which is required to retain `k` previous values; +let `buf` be the global variable name of the ring-buffer in the C +program, and `idx` be the global variable name maintaining the +current index into the ring buffer. Then the correspondence +relation is basically that `0 <= idx < k` and +`s[n+i] = buf[(idx+i) mod k]` as `i` ranges from `0 .. k-1`. +By abuse of notation, here we mean that `s[j]` is +the value of the stream expression `s` evaluated at index `j`, +whereas `buf[j]` means the value obtained by reading the `j`th value +of the buffer `buf` from memory. The overall correspondence relation +is a conjunction of statements like this, one for each stream +expression that is realized via a buffer. + +### Implementing the Bisimulation proof steps + +The kind of program correspondence property we desire is a largely +mechanical affair. As the code under consideration is automatically +generated, it has a very regular structure and is specifically +intended to implement the semantics we wich to verify it against. As +such, we expect these proofs to be highly automatable. + +The proof principle of bisimulation itself is not amenable +to reduction to SMT, as if falls outside the first-order theories +those solvers understand. Likewise, the semantics of Copilot +and C might possibly be reducible directly to SMT, but it would be +impractical to do so. However, we can reduce the individual +proof obligations mentioned above into a series of lower-level +logical statements that are amenable to SMT proof by +defining the logical semantics of stream programs, and using +symbolic simulation techniques to interpret the semantics of the +C program. Performing this reduction is the key contribution +of `copilot-verifier`. + +#### Initial state correspondence + +The proof first obligation we must discharge is to show that +the initial states of the two programs correspond. For each +stream `s` there is a corresponding `k`, which is the length of +the ring-buffer implementing it. We must simply verify that +the C program initializes its buffer with the first `k` values +of the stream `s`, and that the `idx` value starts at 0. +Because of the restrictions Copilot places on programs, these +first `k` values must be specified concretely and will not be +able to depend on any external inputs. As such, this step +is quite easily performed, as it requires only direct evaluation +of concrete inputs. + +#### Transition correspondence + +The bulk of the proof effort consists of demonstrating that +the bisimulation relation is preserved by transitions. +In order to do this step, we must begin with totally symbolic +initial states: we know nothing except that we are at some +arbitrary time value `n`, and that the C program buffers +correspond to their stream values as required by the relation. +Thus, we create fresh symbolic variables for the streams +from `n` to `n + k-1`, and for the values of all the involved +external streams at time `n`. Then, we run forward the Copilot +program by evaluating the stream recurrence expression to +compute the value of each stream at time `n+k`. + +Next we set up an initial state of the C program by choosing, +for each ring buffer, an arbitrary value for its current index +within its allowed range, and then writing the variables +corresponding to each stream value into the buffers at +their appropriate offsets. The symbolic simulator is then +invoked to compute the state update effects of the `step()` +function. Afterward, we read the poststate values from the +ring-buffers and verify that they correspond to the stream +values from `n+1` up to `n+k`. + +As part of symbolic simulation, Crucible may also generate +side-conditions that relate to memory safety of the program, or to +error conditions that must be avoided. All of the bisimulation +equivalence conditions and the safety side-conditions will be +submitted to an SMT solver. + +#### Observable effects + +For our purposes, the only observable effects of a Copilot program +relate to any "trigger" functions defined in the spec. Our task is to +show that the generated C code calls the external trigger functions if +and only if the corresponding guard condition is true, and that the +arguments passed to those functions are as expected. +This proof obligation is proved in the same phase along with +the transition relation proof above because the `step()` function +is responsible for both invoking triggers and for performing state +updates. + +The technique we use to perform this aspect of the proof is to +install "override" functions to the external symbols corresponding +to the C trigger functions before we begin symbolic simulation. +In a real system, the external environment would be responsible +for implementing these functions and taking whatever appropriate +action is necessary when the triggers fire. However, we are verifying +the generated code in isolation from its environment, so we have no +implementation in hand. Instead, the override will +essentially implement a stub function that simply captures its +arguments and the path condition under which it was called. +After simulation completes, the captured arguments and path condition +are required to be equivalent to the corresponding trigger guard +and arguments from the Copilot spec. These conditions are +discharged to the SMT solver at the same time as the transition +relation obligations. + +Because of the way we model the trigger functions, we make a number of +implicit assumptions about how the actual implementations of those +functions must behave. The most important of those assumptions is that +the trigger functions must not modify any memory under the control of +the Copilot program, including its ring buffers and stack. We also +assume that the trigger functions are well defined, i.e. they are +memory safe and do not perform any undefined behavior. + +#### Partial operations + +A generated C program may make use of partial operations. These range from +division, which can fail if the second argument is zero, to signed integer +arithmetic, which can overflow and result in undefined behavior. The verifier +has two modes for dealing with partial operations: + +1. Any invocation of a partial operation on undefined inputs in the generated + C program will result in an error, provided that the user did not add an + assertion that assumes this behavior will not occur. + +2. If the generated C program invokes a partial operation on undefined inputs, + the verifier will check if this coincides with a corresponding invocation + of a partial operation in the Copilot spec. If so, the verification will + succeed. In other words, the verifier will check that the spec and the + C program are "crash-equivalent". + +The verifier uses mode (1) by default, but mode (2) can be enabled by using +`Copilot.Verifier.verifyWithOptions sideCondVerifierOptions`. In this mode, +the verifier will analyze any invocation of a operation which could be partial +and generate a side condition that this operation will only be invoked on +well defined inputs. During the transition step of the bisimulation proof, +the verifier will add these side conditions as assumptions. Therefore, if +simulating the C program generates any side conditions due to invoking partial +operations, these side conditions from the C program should be dischargeable +using the corresponding side conditions from the Copilot spec. + +Mode (2) has the caveat that `clang` may compile C code to LLVM bitcode in +which a partial function is no longer applied to undefined inputs. For +instance, `clang` will sometimes promote 16-bit integer values to 32 bits +before performing arithmetic on them. This can turn an operation that would +result in signed 16-bit integer overflow into a 32-bit integer operation +that does _not_ overflow, for instance. + +### Caveats About the Verifier + +We rely on the `clang` compiler front-end to consume C source files +and produce LLVM intermediate language, which then becomes the input +to the later verification steps. To the extent that the input program +is not fully-portable C, `clang` may make implementation-specific +decisions about how to compile the program which might be made +different if compiled by a different compiler (e.g. `gcc`). We expect +this aspect to be mitigated by the fact that Copilot programs are +automatically generated into a rather simple subset of the C language, +and is designed to be as simple as possible. +Any code-generation bugs in `clang` itself may affect the soundness +of our verifier. Again, however, Copilot generates a well-understood +subset of the language, and we expect `clang` to be well-tested on +the code patterns produced. + +The semantics of LLVM bitcode, as encoded in the `crucible-llvm` +package, may have errors that affect soundness. We mitigate this risk +by testing our semantics against a corpus of verification problems +produced for the SV-COMP verification competition, paying special +attention to any soundness issues that arise. `Crux`, a standalone +verification system based on `crucible-llvm`, was a participant in the +2022 edition of SV-COMP. + +The semantics of Copilot programs, as encoded in the +`Copilot.Theorem.What4` module may have errors that affect soundness. +For the moment we do not have an effective mitigation strategy for +this risk other than manual examination and comparison against the +intended semantics of Copilot, as encoded in the interpreter. + +There is limited SMT solver support for floating-point values, +especially for transcendental functions like the trig primitives. As a +result, we reason about floating point expressions via uninterpreted +functions. In other words, we leave the semantics of the +floating-point operations totally abstract, and simply verify that the +Copilot program and the corresponding C program apply the same +operations in the same order. This is sound, but leaves the possibility +that the compiler will apply some correct transformation to +floating-point expressions that we are nonetheless unable to verify. +However, on low optimizations and without the `--fast-math` flag, +compilers generally (and `clang` in particular) are very reluctant to +rearrange floating-point code, and the verifications generally succeed. diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal new file mode 100644 index 000000000..f7151dfeb --- /dev/null +++ b/copilot-verifier/copilot-verifier.cabal @@ -0,0 +1,152 @@ +Cabal-version: 2.2 +Name: copilot-verifier +Version: 4.4 +Author: Galois Inc. +Maintainer: rscott@galois.com +Copyright: (c) Galois, Inc 2021-2024 +License: BSD-3-Clause +License-file: LICENSE +Build-type: Simple +Category: Language +Synopsis: System for verifying the correctness of generated Copilot programs +Description: + @copilot-verifier@ is an add-on to the [Copilot Stream + DSL](https://copilot-language.github.io) for verifying the correctness of C + code generated by the @copilot-c99@ package. + . + @copilot-verifier@ uses the [Crucible symbolic + simulator](https://github.com/galoisinc/crucible) to interpret the semantics + of the generated C program and and to produce verification conditions + sufficient to guarantee that the meaning of the generated program corresponds + in a precise way to the meaning of the original stream specification. The + generated verification conditions are then dispatched to SMT solvers to be + automatically solved. +extra-doc-files: CHANGELOG, README.md + +source-repository head + type: git + location: https://github.com/GaloisInc/copilot-verifier + subdir: copilot-verifier + +common bldflags + ghc-options: -Wall + -Werror=incomplete-patterns + -Werror=missing-methods + -Werror=overlapping-patterns + -Wpartial-fields + -Wincomplete-uni-patterns + ghc-prof-options: -O2 -fprof-auto-exported + default-language: Haskell2010 + default-extensions: + NondecreasingIndentation + build-depends: + aeson >= 1.5 && < 2.3, + base >= 4.8 && < 4.20, + bv-sized >= 1.0.0 && < 1.1, + bytestring, + containers >= 0.5.9.0, + copilot-c99 >= 4.4 && < 4.5, + copilot-core >= 4.4 && < 4.5, + copilot-theorem >= 4.4 && < 4.5, + crucible >= 0.7.1 && < 0.8, + crucible-llvm >= 0.7 && < 0.8, + crux >= 0.7.1 && < 0.8, + crux-llvm >= 0.9 && < 0.10, + filepath, + lens, + llvm-pretty >= 0.12.1.0 && < 0.13, + mtl, + panic >= 0.3, + parameterized-utils >= 2.1.4 && < 2.2, + prettyprinter >= 1.7.0, + text, + transformers, + vector, + what4 >= 1.6.1 && < 1.7 + +library + import: bldflags + + hs-source-dirs: src + exposed-modules: + Copilot.Verifier + Copilot.Verifier.FloatMode + Copilot.Verifier.Log + Copilot.Verifier.Solver + +library copilot-verifier-examples + import: bldflags + + hs-source-dirs: examples + build-depends: + case-insensitive, + copilot >= 4.4 && < 4.5, + copilot-language >= 4.4 && < 4.5, + copilot-libraries >= 4.4 && < 4.5, + copilot-prettyprinter >= 4.4 && < 4.5, + copilot-verifier + exposed-modules: + Copilot.Verifier.Examples + + Copilot.Verifier.Examples.ShouldFail.Partial.AbsIntMin + Copilot.Verifier.Examples.ShouldFail.Partial.AddSignedWrap + Copilot.Verifier.Examples.ShouldFail.Partial.DivByZero + Copilot.Verifier.Examples.ShouldFail.Partial.IndexOutOfBounds + Copilot.Verifier.Examples.ShouldFail.Partial.ModByZero + Copilot.Verifier.Examples.ShouldFail.Partial.MulSignedWrap + Copilot.Verifier.Examples.ShouldFail.Partial.ShiftLTooLarge + Copilot.Verifier.Examples.ShouldFail.Partial.ShiftRTooLarge + Copilot.Verifier.Examples.ShouldFail.Partial.SubSignedWrap + + Copilot.Verifier.Examples.ShouldPass.Array + Copilot.Verifier.Examples.ShouldPass.ArrayGen + Copilot.Verifier.Examples.ShouldPass.ArrayOfStructs + Copilot.Verifier.Examples.ShouldPass.ArrayTriggerArgument + Copilot.Verifier.Examples.ShouldPass.Arith + Copilot.Verifier.Examples.ShouldPass.Clock + Copilot.Verifier.Examples.ShouldPass.Counter + Copilot.Verifier.Examples.ShouldPass.Engine + Copilot.Verifier.Examples.ShouldPass.FPNegation + Copilot.Verifier.Examples.ShouldPass.FPOps + Copilot.Verifier.Examples.ShouldPass.Heater + Copilot.Verifier.Examples.ShouldPass.IntOps + Copilot.Verifier.Examples.ShouldPass.Partial.AbsIntMin + Copilot.Verifier.Examples.ShouldPass.Partial.AddSignedWrap + Copilot.Verifier.Examples.ShouldPass.Partial.DivByZero + Copilot.Verifier.Examples.ShouldPass.Partial.IndexOutOfBounds + Copilot.Verifier.Examples.ShouldPass.Partial.ModByZero + Copilot.Verifier.Examples.ShouldPass.Partial.MulSignedWrap + Copilot.Verifier.Examples.ShouldPass.Partial.ShiftLTooLarge + Copilot.Verifier.Examples.ShouldPass.Partial.ShiftRTooLarge + Copilot.Verifier.Examples.ShouldPass.Partial.SubSignedWrap + Copilot.Verifier.Examples.ShouldPass.Structs + Copilot.Verifier.Examples.ShouldPass.UpdateArray + Copilot.Verifier.Examples.ShouldPass.UpdateStruct + Copilot.Verifier.Examples.ShouldPass.Voting + Copilot.Verifier.Examples.ShouldPass.WCV + +executable verify-examples + import: bldflags + + hs-source-dirs: exe + main-is: VerifyExamples.hs + build-depends: + case-insensitive, + copilot-verifier, + copilot-verifier-examples, + optparse-applicative + +test-suite copilot-verifier-test + import: bldflags + + type: exitcode-stdio-1.0 + hs-source-dirs: test + build-depends: + case-insensitive, + copilot-verifier, + copilot-verifier-examples, + silently >= 1.2, + tasty >= 0.10, + tasty-expected-failure >= 0.12, + tasty-hunit >= 0.10 + main-is: Test.hs diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples.hs b/copilot-verifier/examples/Copilot/Verifier/Examples.hs new file mode 100644 index 000000000..7e593a7e9 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE OverloadedStrings #-} +module Copilot.Verifier.Examples + ( shouldFailExamples + , shouldPassExamples + ) where + +import qualified Data.CaseInsensitive as CI +import Data.CaseInsensitive (CI) +import qualified Data.Map as Map +import Data.Map (Map) +import Data.Text (Text) + +import Copilot.Verifier (Verbosity) +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.AbsIntMin as Fail.AbsIntMin +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.AddSignedWrap as Fail.AddSignedWrap +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.DivByZero as Fail.DivByZero +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.IndexOutOfBounds as Fail.IndexOutOfBounds +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.ModByZero as Fail.ModByZero +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.MulSignedWrap as Fail.MulSignedWrap +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.ShiftLTooLarge as Fail.ShiftLTooLarge +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.ShiftRTooLarge as Fail.ShiftRTooLarge +import qualified Copilot.Verifier.Examples.ShouldFail.Partial.SubSignedWrap as Fail.SubSignedWrap +import qualified Copilot.Verifier.Examples.ShouldPass.Array as Array +import qualified Copilot.Verifier.Examples.ShouldPass.ArrayGen as ArrayGen +import qualified Copilot.Verifier.Examples.ShouldPass.ArrayOfStructs as ArrayOfStructs +import qualified Copilot.Verifier.Examples.ShouldPass.ArrayTriggerArgument as ArrayTriggerArgument +import qualified Copilot.Verifier.Examples.ShouldPass.Arith as Arith +import qualified Copilot.Verifier.Examples.ShouldPass.Clock as Clock +import qualified Copilot.Verifier.Examples.ShouldPass.Counter as Counter +import qualified Copilot.Verifier.Examples.ShouldPass.Engine as Engine +import qualified Copilot.Verifier.Examples.ShouldPass.FPNegation as FPNegation +import qualified Copilot.Verifier.Examples.ShouldPass.FPOps as FPOps +import qualified Copilot.Verifier.Examples.ShouldPass.Heater as Heater +import qualified Copilot.Verifier.Examples.ShouldPass.IntOps as IntOps +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.AbsIntMin as Pass.AbsIntMin +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.AddSignedWrap as Pass.AddSignedWrap +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.IndexOutOfBounds as Pass.IndexOutOfBounds +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.DivByZero as Pass.DivByZero +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.ModByZero as Pass.ModByZero +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.MulSignedWrap as Pass.MulSignedWrap +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.ShiftLTooLarge as Pass.ShiftLTooLarge +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.ShiftRTooLarge as Pass.ShiftRTooLarge +import qualified Copilot.Verifier.Examples.ShouldPass.Partial.SubSignedWrap as Pass.SubSignedWrap +import qualified Copilot.Verifier.Examples.ShouldPass.Structs as Structs +import qualified Copilot.Verifier.Examples.ShouldPass.UpdateArray as UpdateArray +import qualified Copilot.Verifier.Examples.ShouldPass.UpdateStruct as UpdateStruct +import qualified Copilot.Verifier.Examples.ShouldPass.Voting as Voting +import qualified Copilot.Verifier.Examples.ShouldPass.WCV as WCV + +shouldFailExamples :: Verbosity -> Map (CI Text) (IO ()) +shouldFailExamples verb = Map.fromList + [ -- Partial operation tests + example "AbsIntMin-fail" (Fail.AbsIntMin.verifySpec verb) + , example "AddSignedWrap-fail" (Fail.AddSignedWrap.verifySpec verb) + , example "DivByZero-fail" (Fail.DivByZero.verifySpec verb) + , example "IndexOutOfBounds-fail" (Fail.IndexOutOfBounds.verifySpec verb) + , example "ModByZero-fail" (Fail.ModByZero.verifySpec verb) + , example "MulSignedWrap-fail" (Fail.MulSignedWrap.verifySpec verb) + , example "ShiftLTooLarge-fail" (Fail.ShiftLTooLarge.verifySpec verb) + , example "ShiftRTooLarge-fail" (Fail.ShiftRTooLarge.verifySpec verb) + , example "SubSignedWrap-fail" (Fail.SubSignedWrap.verifySpec verb) + ] + +shouldPassExamples :: Verbosity -> Map (CI Text) (IO ()) +shouldPassExamples verb = Map.fromList + [ example "Array" (Array.verifySpec verb) + , example "ArrayGen" (ArrayGen.verifySpec verb) + , example "ArrayOfStructs" (ArrayOfStructs.verifySpec verb) + , example "ArrayTriggerArgument" (ArrayTriggerArgument.verifySpec verb) + , example "Arith" (Arith.verifySpec verb) + , example "Clock" (Clock.verifySpec verb) + , example "Counter" (Counter.verifySpec verb) + , example "Engine" (Engine.verifySpec verb) + , example "FPNegation" (FPNegation.verifySpec verb) + , example "FPOps" (FPOps.verifySpec verb) + , example "Heater" (Heater.verifySpec verb) + , example "IntOps" (IntOps.verifySpec verb) + , example "Structs" (Structs.verifySpec verb) + , example "UpdateArray" (UpdateArray.verifySpec verb) + , example "UpdateStruct" (UpdateStruct.verifySpec verb) + , example "Voting" (Voting.verifySpec verb) + , example "WCV" (WCV.verifySpec verb) + + -- Partial operation tests + , example "AbsIntMin-pass" (Pass.AbsIntMin.verifySpec verb) + , example "AddSignedWrap-pass" (Pass.AddSignedWrap.verifySpec verb) + , example "DivByZero-pass" (Pass.DivByZero.verifySpec verb) + , example "IndexOutOfBounds-pass" (Pass.IndexOutOfBounds.verifySpec verb) + , example "ModByZero-pass" (Pass.ModByZero.verifySpec verb) + , example "MulSignedWrap-pass" (Pass.MulSignedWrap.verifySpec verb) + , example "ShiftLTooLarge-pass" (Pass.ShiftLTooLarge.verifySpec verb) + , example "ShiftRTooLarge-pass" (Pass.ShiftRTooLarge.verifySpec verb) + , example "SubSignedWrap-pass" (Pass.SubSignedWrap.verifySpec verb) + ] + +example :: Text -> IO () -> (CI Text, IO ()) +example name action = (CI.mk name, action) diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AbsIntMin.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AbsIntMin.hs new file mode 100644 index 000000000..70147e5a4 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AbsIntMin.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @notIntMin@ property, which is needed to prevent undefined behavior when +-- invoking the 'abs' function. +module Copilot.Verifier.Examples.ShouldFail.Partial.AbsIntMin where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int32 + stream = extern "stream" Nothing + + _ <- prop "notIntMin" (forAll (stream > constI32 minBound)) + trigger "streamAbs" (abs stream == 1) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["notIntMin"] + [] + "absIntMinFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AddSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AddSignedWrap.hs new file mode 100644 index 000000000..5ae869641 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/AddSignedWrap.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @notIntMax@ property, which is needed to prevent signed integer overflow. +module Copilot.Verifier.Examples.ShouldFail.Partial.AddSignedWrap where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int32 + stream = extern "stream" Nothing + + _ <- prop "notIntMax" (forAll (stream < constI32 maxBound)) + trigger "streamAddSigned" ((stream + 1) == 1) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["notIntMax"] + [] + "addSignedWrapFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/DivByZero.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/DivByZero.hs new file mode 100644 index 000000000..53786931a --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/DivByZero.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @nonzero@ property, which is needed to prevent a division-by-zero error. +module Copilot.Verifier.Examples.ShouldFail.Partial.DivByZero where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int16 + stream = extern "stream" Nothing + + _ <- prop "nonzero" (forAll (stream /= 0)) + trigger "streamDiv" ((stream `div` stream) == 1) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["nonzero"] + [] + "divByZeroFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/IndexOutOfBounds.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/IndexOutOfBounds.hs new file mode 100644 index 000000000..93f3812af --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/IndexOutOfBounds.hs @@ -0,0 +1,31 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @withinBounds@ property, which is needed to prevent an out-of-bounds array index. +module Copilot.Verifier.Examples.ShouldFail.Partial.IndexOutOfBounds where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream1 :: Stream (Array 2 Int16) + stream1 = constant (array [27, 42]) + + stream2 :: Stream Word32 + stream2 = extern "stream2" Nothing + + _ <- prop "withinBounds" (forAll (stream2 < constW32 2)) + trigger "streamIndex" ((stream1 ! stream2) == 1) [arg stream1, arg stream2] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["withinBounds"] + [] + "indexFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ModByZero.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ModByZero.hs new file mode 100644 index 000000000..c42f16c4e --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ModByZero.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @nonzero@ property, which is needed to prevent a division-by-zero error. +module Copilot.Verifier.Examples.ShouldFail.Partial.ModByZero where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int16 + stream = extern "stream" Nothing + + _ <- prop "nonzero" (forAll (stream /= 0)) + trigger "streamMod" ((stream `mod` stream) == 1) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["nonzero"] + [] + "modByZeroFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/MulSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/MulSignedWrap.hs new file mode 100644 index 000000000..41f746b34 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/MulSignedWrap.hs @@ -0,0 +1,31 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @withinRange@ property, which is needed to prevent signed integer underflow or overflow. +module Copilot.Verifier.Examples.ShouldFail.Partial.MulSignedWrap where + +import qualified Prelude as P + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int32 + stream = extern "stream" Nothing + + _ <- prop "withinRange" (forAll + (constI32 ((minBound P.+ 1) P.* 2) < stream + && stream < constI32 ((maxBound P.- 1) `P.div` 2))) + trigger "streamMulSigned" ((stream * 2) == 2) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["withinRange"] + [] + "mulSignedWrapFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftLTooLarge.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftLTooLarge.hs new file mode 100644 index 000000000..dbdcd8b0f --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftLTooLarge.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @lessThanBitWidth@ property, which is needed to prevent shifting by too +-- large of a value. +module Copilot.Verifier.Examples.ShouldFail.Partial.ShiftLTooLarge where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream1 :: Stream Int32 + stream1 = extern "stream1" Nothing + + stream2 :: Stream Int64 + stream2 = extern "stream2" Nothing + + _ <- prop "lessThanBitWidth" (forAll + (constI64 0 <= stream2 && stream2 < constI64 32)) + trigger "streamShiftL" ((stream1 .<<. stream2) == 1) [arg stream1, arg stream2] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["lessThanBitWidth"] + [] + "shiftLTooLargeFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftRTooLarge.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftRTooLarge.hs new file mode 100644 index 000000000..12fe7c1f1 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/ShiftRTooLarge.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @lessThanBitWidth@ property, which is needed to prevent shifting by too +-- large of a value. +module Copilot.Verifier.Examples.ShouldFail.Partial.ShiftRTooLarge where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream1 :: Stream Int32 + stream1 = extern "stream1" Nothing + + stream2 :: Stream Int64 + stream2 = extern "stream2" Nothing + + _ <- prop "lessThanBitWidth" (forAll + (constI64 0 <= stream2 && stream2 < constI64 32)) + trigger "streamShiftR" ((stream1 .<<. stream2) == 1) [arg stream1, arg stream2] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["lessThanBitWidth"] + [] + "shiftRTooLargeFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/SubSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/SubSignedWrap.hs new file mode 100644 index 000000000..5503b1743 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldFail/Partial/SubSignedWrap.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will fail to verify since the verification does not assume the +-- @notIntMin@ property, which is needed to prevent signed integer underflow. +module Copilot.Verifier.Examples.ShouldFail.Partial.SubSignedWrap where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let stream :: Stream Int32 + stream = extern "stream" Nothing + + _ <- prop "notIntMin" (forAll (stream > constI32 minBound)) + trigger "streamSubSigned" ((stream - 1) == 1) [arg stream] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings + -- ["notIntMin"] + [] + "subSignedWrapFail" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Arith.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Arith.hs new file mode 100644 index 000000000..58b75fb92 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Arith.hs @@ -0,0 +1,52 @@ + +{-# LANGUAGE RebindableSyntax #-} + +module Copilot.Verifier.Examples.ShouldPass.Arith where + +import Control.Monad (when) +import qualified Prelude as P + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity(..), VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Copilot.Theorem.What4 (prove, Solver(..)) + +-- The largest unsigned 32-bit prime +lastPrime :: Stream Word32 +lastPrime = 31 +--lastPrime = 4294967291 -- Whelp, this prime seems too big for the solvers to handle well + +multRingSpec :: Spec +multRingSpec = do + _ <- prop "clamp nonzero" (forAll ((clamp > 0) && (clamp < lastPrime))) + _ <- prop "reduced" (forAll (acc < lastPrime)) + _ <- prop "nonzero" (forAll (acc > 0 && (acc < lastPrime))) + + trigger "outofrange" (not (acc > 0 && acc < lastPrime)) [arg acc] + + return () + + where + -- a stream of external values + vals = externW32 "values" Nothing + + -- Generate a value in [1, lastPrime), which + -- is the multiplictive group of Z_p + clamp :: Stream Word32 + clamp = (vals `mod` (lastPrime - 1)) + 1 + + -- Successively multiply new values + acc :: Stream Word32 + acc = [1] ++ unsafeCast ((cast acc * cast clamp) `mod` (cast lastPrime :: Stream Word64)) + +verifySpec :: Verbosity -> IO () +verifySpec verb = + do s <- reify multRingSpec + r <- prove Z3 s + when (verb P.>= Default) $ + print r + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings ["reduced"] "multRingSpec" s + +--verifySpec _ = interpret 10 engineMonitor diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Array.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Array.hs new file mode 100644 index 000000000..365d54c4b --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Array.hs @@ -0,0 +1,40 @@ +-------------------------------------------------------------------------------- +-- Copyright © 2019 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- | This is a simple example for arrays. As a program, it does not make much +-- sense, however it shows of the features of arrays nicely. + +-- | Enable compiler extension for type-level data, necesary for the array +-- length. + +{-# LANGUAGE RebindableSyntax #-} +{-# LANGUAGE DataKinds #-} + +module Copilot.Verifier.Examples.ShouldPass.Array where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +-- Lets define an array of length 2. +-- Make the buffer of the streams 3 elements long. +arr :: Stream (Array 2 Bool) +arr = [ array [True, False] + , array [True, True] + , array [False, False]] ++ arr + +spec :: Spec +spec = do + -- A trigger that fires 'func' when the first element of 'arr' is True. + -- It passes the current value of arr as an argument. + -- The prototype of 'func' would be: + -- void func (int8_t arg[3]); + trigger "func" (arr ! 0) [arg arr] + +-- Compile the spec +verifySpec :: Verbosity -> IO () +verifySpec verb = reify spec >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "array" +-- verifySpec _ = interpret 30 spec diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayGen.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayGen.hs new file mode 100644 index 000000000..115c753cb --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayGen.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DataKinds #-} +module Copilot.Verifier.Examples.ShouldPass.ArrayGen where + +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Language.Copilot +import qualified Prelude hiding ((++), (>)) + +spec :: Spec +spec = trigger "f" (stream ! 0 > 0) [arg stream] + where + stream :: Stream (Array 2 Int16) + stream = [array [3,4]] ++ rest + + rest :: Stream (Array 2 Int16) + rest = constant $ array [5,6] + +verifySpec :: Verbosity -> IO () +verifySpec verb = reify spec >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "arrayGen" diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayOfStructs.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayOfStructs.hs new file mode 100644 index 000000000..b983531f0 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayOfStructs.hs @@ -0,0 +1,29 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +module Copilot.Verifier.Examples.ShouldPass.ArrayOfStructs where + +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Language.Copilot + +data S = S { field :: Field "field" Int16 } + +instance Struct S where + typeName _ = "s" + toValues s = [Value Int16 (field s)] + +instance Typed S where + typeOf = Struct (S (Field 0)) + +spec :: Spec +spec = trigger "f" ((stream ! 0)#field == 27) [arg stream] + where + stream :: Stream (Array 2 S) + stream = [array [S (Field 27), S (Field 42)]] ++ stream + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "arrayOfStructs" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayTriggerArgument.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayTriggerArgument.hs new file mode 100644 index 000000000..5c06b626a --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/ArrayTriggerArgument.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE DataKinds #-} + +-- | A regression test for +-- . +module Copilot.Verifier.Examples.ShouldPass.ArrayTriggerArgument where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = trigger "f" true [arg stream] + where + stream :: Stream (Array 2 Int16) + stream = constant (array [3,4]) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions + defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "arrayTriggerArgument" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Clock.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Clock.hs new file mode 100644 index 000000000..7f9d8d303 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Clock.hs @@ -0,0 +1,46 @@ +-------------------------------------------------------------------------------- +-- Copyright © 2019 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- | Example showing usage of clocks to generate periodically recurring truth +-- values. + +module Copilot.Verifier.Examples.ShouldPass.Clock where + +import Control.Monad (when) +import qualified Prelude as P + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity(..), VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Copilot.Theorem.What4 (prove, Solver(..)) + +-- | We need to force a type for the argument of `period`. +p :: Word8 +p = 5 + +-- | Both have the same period, but a different phase. +clkStream :: Stream Bool +clkStream = clk (period p) (phase 0) + +clkStream' :: Stream Bool +clkStream' = clk (period p) (phase 2) + +spec :: Spec +spec = do + observer "clk" clkStream + observer "clk'" clkStream' + _ <- prop "clksPhase" (forAll (clkStream == drop 2 clkStream')) + _ <- prop "clksDistinct" (forAll (not (clkStream && clkStream'))) + trigger "clksHigh" (clkStream && clkStream') [] + + +verifySpec :: Verbosity -> IO () +verifySpec verb = + do s <- reify spec + r <- prove Z3 s + when (verb P.>= Default) $ + print r + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "clock" s diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Counter.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Counter.hs new file mode 100644 index 000000000..708ea662f --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Counter.hs @@ -0,0 +1,56 @@ +------------------------------------------------------------------------------- +-- Copyright © 2019 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- | Example showing an implementation of a resettable counter. + +{-# LANGUAGE RebindableSyntax #-} + +module Copilot.Verifier.Examples.ShouldPass.Counter where + +import Control.Monad (when) +import qualified Prelude as P + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity(..), VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Copilot.Theorem.What4 (prove, Solver(..)) + +-- A resettable counter +counter :: (Typed a, Integral a) => Stream Bool -> Stream Bool -> Stream a +counter inc reset = cnt + where + cnt = if reset then 0 + else if inc then z + 1 + else z + z = [0] ++ cnt + +-- Counter that resets when it reaches 256 +bytecounter :: Stream Int32 +bytecounter = counter true (resetcounter `mod` 256 == 0) + +resetcounter :: Stream Word32 +resetcounter = counter true false + +bytecounter2 :: Stream Int32 +bytecounter2 = counter true ([False] ++ bytecounter2 == 255) + +spec :: Spec +spec = + do _ <- prop "range" (forAll (bytecounter == unsafeCast (resetcounter `mod` 256))) + _ <- prop "range2" (forAll (0 <= bytecounter2 && bytecounter2 <= 255)) + _ <- prop "same" (forAll ((0 <= bytecounter2 && bytecounter2 <= 255) && + (bytecounter == unsafeCast (resetcounter `mod` 256)) && + (bytecounter == bytecounter2))) + trigger "counter" true [arg $ bytecounter, arg $ bytecounter2] + +verifySpec :: Verbosity -> IO () +-- verifSpec _ = interpret 1280 spec +verifySpec verb = + do s <- reify spec + r <- prove Z3 s + when (verb P.>= Default) $ + print r + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings ["range", "range2"] "counter" s diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Engine.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Engine.hs new file mode 100644 index 000000000..74e7509ee --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Engine.hs @@ -0,0 +1,44 @@ +-------------------------------------------------------------------------------- +-- Copyright © 2011 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- | Example implementing an engine cooling control system. + +{-# LANGUAGE RebindableSyntax #-} + +module Copilot.Verifier.Examples.ShouldPass.Engine where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +import qualified Prelude as P + +{- If the majority of the engine temperature probes exeeds 250 degrees, then + - the cooler is engaged and remains engaged until the majority of the engine + - temperature probes drop to 250 or below. Otherwise, trigger an immediate + - shutdown of the engine. +-} + +engineMonitor :: Spec +engineMonitor = do + trigger "shutoff" (not ok) [arg maj] + + where + vals = [ externW8 "tmp_probe_0" two51 + , externW8 "tmp_probe_1" two51 + , externW8 "tmp_probe_2" zero] + exceed = map (> 250) vals + maj = majority exceed + checkMaj = aMajority exceed maj + ok = alwaysBeen ((maj && checkMaj) ==> extern "cooler" cooler) + + two51 = Just $ [251, 251] P.++ repeat (250 :: Word8) + zero = Just $ repeat (0 :: Word8) + cooler = Just $ [True, True] P.++ repeat False + +verifySpec :: Verbosity -> IO () +verifySpec verb = reify engineMonitor >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "engine" +--verifySpec _ = interpret 10 engineMonitor diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPNegation.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPNegation.hs new file mode 100644 index 000000000..5078d0775 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPNegation.hs @@ -0,0 +1,62 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will not succeed when 'smtFloatMode' is 'FloatUninterpreted', as Clang +-- will turn @input /= 30.0@ below into a more complicated expression that also +-- checks if @30.0@ is NaN or not. This is logically equivalent to the original +-- specification, but an SMT solver cannot conclude this when all of the +-- floating-point operations involved are treated as uninterpreted functions. +-- +-- To make this work, we set 'smtFloatMode' to 'FloatIEEE' instead. This works +-- because all of the floating-point operations in the specification below are +-- native to SMT-LIB. +module Copilot.Verifier.Examples.ShouldPass.FPNegation where + +import Copilot.Compile.C99 (mkDefaultCSettings) +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.FloatMode (FloatMode(..)) +import qualified Copilot.Library.PTLTL as PTLTL +import Language.Copilot + +input :: Stream Float +input = extern "input" Nothing + +-- | MyProperty +-- @ +-- Input is never 30.0 +-- @ +propMyProperty :: Stream Bool +propMyProperty = PTLTL.alwaysBeen (input /= 30.0) + +-- | Clock that increases in one-unit steps. +clock :: Stream Int64 +clock = [0] ++ (clock + 1) + +-- | First Time Point +ftp :: Stream Bool +ftp = [True] ++ false + +pre :: Stream Bool -> Stream Bool +pre = ([False] ++) + +tpre :: Stream Bool -> Stream Bool +tpre = ([True] ++) + +notPreviousNot :: Stream Bool -> Stream Bool +notPreviousNot = not . PTLTL.previous . not + +-- | Complete specification. Calls C handler functions when properties are +-- violated. +spec :: Spec +spec = do + trigger "handlerMyProperty" (not propMyProperty) [] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions + (defaultVerifierOptions + { verbosity = verb + , smtFloatMode = FloatIEEE + }) + mkDefaultCSettings [] "fpNegation" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPOps.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPOps.hs new file mode 100644 index 000000000..02bd0130a --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/FPOps.hs @@ -0,0 +1,88 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +module Copilot.Verifier.Examples.ShouldPass.FPOps where + +import Copilot.Compile.C99 (mkDefaultCSettings) +import qualified Copilot.Language.Stream as Copilot +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Data.Proxy (Proxy(..)) +import Language.Copilot +import qualified Prelude as P + +mkSpecFor :: forall a proxy. (RealFloat a, Typed a) => proxy a -> String -> Spec +mkSpecFor _ nameSuffix = do + let mkName :: String -> String + mkName namePrefix = namePrefix P.++ nameSuffix + + stream :: Stream a + stream = extern (mkName "stream") Nothing + + triggerOp1 (mkName "abs") abs stream + triggerOp1 (mkName "signum") signum stream + triggerOp1 (mkName "recip") recip stream + triggerOp1 (mkName "exp") exp stream + triggerOp1 (mkName "sqrt") sqrt stream + triggerOp1 (mkName "log") log stream + triggerOp1 (mkName "sin") sin stream + triggerOp1 (mkName "tan") tan stream + triggerOp1 (mkName "cos") cos stream + triggerOp1 (mkName "asin") asin stream + triggerOp1 (mkName "atan") atan stream + triggerOp1 (mkName "acos") acos stream + triggerOp1 (mkName "sinh") sinh stream + triggerOp1 (mkName "tanh") tanh stream + triggerOp1 (mkName "cosh") cosh stream + triggerOp1 (mkName "asinh") asinh stream + triggerOp1 (mkName "atanh") atanh stream + triggerOp1 (mkName "acosh") acosh stream + triggerOp1 (mkName "ceiling") Copilot.ceiling stream + triggerOp1 (mkName "floor") Copilot.floor stream + + triggerOp2 (mkName "add") (+) stream + triggerOp2 (mkName "sub") (-) stream + triggerOp2 (mkName "mul") (*) stream + triggerOp2 (mkName "div") (/) stream + triggerOp2 (mkName "pow") (**) stream + triggerOp2 (mkName "logBase") logBase stream + triggerOp2 (mkName "atan2") Copilot.atan2 stream + +spec :: Spec +spec = do + mkSpecFor (Proxy @Float) "F" + mkSpecFor (Proxy @Double) "D" + +triggerOp1 :: (RealFloat a, Typed a) => + String -> + (Stream a -> Stream a) -> + Stream a -> + Spec +triggerOp1 name op stream = + trigger (name P.++ "Trigger") (testOp1 op stream) [arg stream] + +triggerOp2 :: (RealFloat a, Typed a) => + String -> + (Stream a -> Stream a -> Stream a) -> + Stream a -> + Spec +triggerOp2 name op stream = + trigger (name P.++ "Trigger") (testOp2 op stream) [arg stream] + +testOp1 :: (RealFloat a, Typed a) => + (Stream a -> Stream a) -> Stream a -> Stream Bool +testOp1 op stream = + -- NB: Use (>=) rather than (==) here, as floating-point equality gets weird + -- due to NaNs. + op stream >= op stream + +testOp2 :: (RealFloat a, Typed a) => + (Stream a -> Stream a -> Stream a) -> Stream a -> Stream Bool +testOp2 op stream = + op stream stream >= op stream stream + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "fpOps" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Heater.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Heater.hs new file mode 100644 index 000000000..b2c9101ff --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Heater.hs @@ -0,0 +1,60 @@ +-------------------------------------------------------------------------------- +-- Copyright 2019 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- This is a simple example with basic usage. It implements a simple home +-- heating system: It heats when temp gets too low, and stops when it is high +-- enough. It read temperature as a byte (range -50C to 100C) and translates +-- this to Celcius. + +module Copilot.Verifier.Examples.ShouldPass.Heater where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.PrettyPrint as PP +--import Copilot.Language.Prelude + +import Copilot.Verifier ( Verbosity(..), VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +import qualified Prelude as P +import Control.Monad (when) + +-- External temperature as a byte, range of -50C to 100C +temp :: Stream Word8 +temp = extern "temperature" Nothing + +-- Calculate temperature in Celcius. +-- We need to cast the Word8 to a Float. Note that it is an unsafeCast, as there +-- is no direct relation between Word8 and Float. +ctemp :: Stream Float +ctemp = (unsafeCast temp * constant (150.0/255.0)) - constant 50.0 + +-- width of the sliding window +window :: Int +window = 5 + +-- Compute the sliding average of the last 5 temps +-- (Here, 19.5 is the average of 18.0 and 21.0, the two temperature extremes +-- that we check for in the spec.) +avgTemp :: Stream Float +avgTemp = (sum window (replicate window 19.5 ++ ctemp)) / fromIntegral window + +spec :: Spec +spec = do + trigger "heaton" (avgTemp < 18.0) [arg avgTemp] + trigger "heatoff" (avgTemp > 21.0) [arg avgTemp] + +-- Compile the spec +verifySpec :: Verbosity -> IO () +verifySpec verb = + do rspec <- reify spec + when (verb P.>= Default) $ putStrLn (PP.prettyPrint rspec) + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "heater" + rspec + +{- + do spec' <- reify spec + putStrLn $ prettyPrintDot spec' +-} diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/IntOps.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/IntOps.hs new file mode 100644 index 000000000..f4c4a1db4 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/IntOps.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module Copilot.Verifier.Examples.ShouldPass.IntOps where + +import Copilot.Compile.C99 (mkDefaultCSettings) +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) +import Language.Copilot +import qualified Prelude as P + +spec :: Spec +spec = do + let stream :: Stream Int16 + stream = extern "stream" Nothing + + shiftBy :: Stream Int16 + shiftBy = extern "shiftBy" Nothing + + _ <- prop "nonzero" (forAll (stream /= 0)) + _ <- prop "shiftByBits" (forAll (0 <= shiftBy && shiftBy < 16)) + + triggerOp1 "abs" abs stream + triggerOp1 "signum" signum stream + triggerOp1 "bwNot" complement stream + + triggerOp2 "add" (+) stream stream + triggerOp2 "sub" (-) stream stream + triggerOp2 "mul" (*) stream stream + triggerOp2 "mod" mod stream stream + triggerOp2 "div" div stream stream + triggerOp2 "bwAnd" (.&.) stream stream + triggerOp2 "bwOr" (.|.) stream stream + triggerOp2 "bwXor" (.^.) stream stream + triggerOp2 "bwShiftL" (.<<.) stream shiftBy + triggerOp2 "bwShiftR" (.>>.) stream shiftBy + +triggerOp1 :: String -> + (Stream Int16 -> Stream Int16) -> + Stream Int16 -> + Spec +triggerOp1 name op stream = + trigger (name P.++ "Trigger") (testOp1 op stream) [arg stream] + +triggerOp2 :: String -> + (Stream Int16 -> Stream Int16 -> Stream Int16) -> + Stream Int16 -> Stream Int16 -> + Spec +triggerOp2 name op stream1 stream2 = + trigger (name P.++ "Trigger") (testOp2 op stream1 stream2) [arg stream1, arg stream2] + +testOp1 :: (Stream Int16 -> Stream Int16) -> Stream Int16 -> Stream Bool +testOp1 op stream = + op stream == op stream + +testOp2 :: (Stream Int16 -> Stream Int16 -> Stream Int16) -> + Stream Int16 -> Stream Int16 -> + Stream Bool +testOp2 op stream1 stream2 = + op stream1 stream2 == op stream1 stream2 + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings ["nonzero", "shiftByBits"] "intOps" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AbsIntMin.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AbsIntMin.hs new file mode 100644 index 000000000..1b27fdeed --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AbsIntMin.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Abs@ +-- operation is well defined precisely when C's @abs@ function is well defined. +module Copilot.Verifier.Examples.ShouldPass.Partial.AbsIntMin where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.AbsIntMin (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "absIntMinPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AddSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AddSignedWrap.hs new file mode 100644 index 000000000..bcf092f65 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/AddSignedWrap.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Add@ +-- operation should overflow on signed integers precisely when C addition does. +module Copilot.Verifier.Examples.ShouldPass.Partial.AddSignedWrap where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.AddSignedWrap (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "addSignedWrapPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/DivByZero.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/DivByZero.hs new file mode 100644 index 000000000..6e5c7a65e --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/DivByZero.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Div@ +-- operation is well defined precisely when C division is well defined. +module Copilot.Verifier.Examples.ShouldPass.Partial.DivByZero where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.DivByZero (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "divByZeroPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/IndexOutOfBounds.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/IndexOutOfBounds.hs new file mode 100644 index 000000000..eb10f24b9 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/IndexOutOfBounds.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's indexing +-- operation should be out of bounds precisely when C array indexes are out of bounds. +module Copilot.Verifier.Examples.ShouldPass.Partial.IndexOutOfBounds where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.IndexOutOfBounds (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "indexPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ModByZero.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ModByZero.hs new file mode 100644 index 000000000..1f9f6bf97 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ModByZero.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Mod@ +-- operation is well defined precisely when C's @%@ operator is well defined. +module Copilot.Verifier.Examples.ShouldPass.Partial.ModByZero where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.ModByZero (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "modByZeroPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/MulSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/MulSignedWrap.hs new file mode 100644 index 000000000..fe70f9451 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/MulSignedWrap.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Mul@ +-- operation should wrap on signed integers precisely when C multiplication does. +module Copilot.Verifier.Examples.ShouldPass.Partial.MulSignedWrap where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.MulSignedWrap (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "mulSignedWrapPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftLTooLarge.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftLTooLarge.hs new file mode 100644 index 000000000..314070096 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftLTooLarge.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @BwShiftL@ +-- operation should only accept second arguments as large as C's @<<@ operation does. +module Copilot.Verifier.Examples.ShouldPass.Partial.ShiftLTooLarge where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.ShiftLTooLarge (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "shiftLTooLargePass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftRTooLarge.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftRTooLarge.hs new file mode 100644 index 000000000..afab4ba46 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/ShiftRTooLarge.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @BwShiftR@ +-- operation should only accept second arguments as large as C's @>>@ operation does. +module Copilot.Verifier.Examples.ShouldPass.Partial.ShiftRTooLarge where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.ShiftRTooLarge (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "shiftRTooLargePass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/SubSignedWrap.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/SubSignedWrap.hs new file mode 100644 index 000000000..9d180bd44 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Partial/SubSignedWrap.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +-- | This will succeed with 'sideCondVerifierOptions', as Copilot's @Sub@ +-- operation should underflow on signed integers precisely when C subtraction does. +module Copilot.Verifier.Examples.ShouldPass.Partial.SubSignedWrap where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , sideCondVerifierOptions, verifyWithOptions ) +import Copilot.Verifier.Examples.ShouldFail.Partial.SubSignedWrap (spec) + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions sideCondVerifierOptions{verbosity = verb} + mkDefaultCSettings + [] + "subSignedWrapPass" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Structs.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Structs.hs new file mode 100644 index 000000000..7f68e4b20 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Structs.hs @@ -0,0 +1,92 @@ +-- | An example showing the usage of the What4 backend in copilot-theorem for +-- structs and arrays. Particular focus is on nested structs. +-- For general usage of structs, refer to the general structs example. + +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +module Copilot.Verifier.Examples.ShouldPass.Structs where + +import Control.Monad (void, forM_, when) +import qualified Prelude as P + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Theorem.What4 +import Copilot.Verifier ( Verbosity(..), VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + + +-- | Definition for `Volts`. +data Volts = Volts + { numVolts :: Field "numVolts" Word16 + , flag :: Field "flag" Bool + } + +-- | `Struct` instance for `Volts`. +instance Struct Volts where + typeName _ = "volts" + toValues vlts = [ Value Word16 (numVolts vlts) + , Value Bool (flag vlts) + ] + +-- | `Volts` instance for `Typed`. +instance Typed Volts where + typeOf = Struct (Volts (Field 0) (Field False)) + +data Battery = Battery + { temp :: Field "temp" Word16 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + +-- | `Battery` instance for `Struct`. +instance Struct Battery where + typeName _ = "battery" + toValues battery = [ Value typeOf (temp battery) + , Value typeOf (volts battery) + , Value typeOf (other battery) + ] + +-- | `Battery` instance for `Typed`. Note that `undefined` is used as an +-- argument to `Field`. This argument is never used, so `undefined` will never +-- throw an error. +instance Typed Battery where + typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + +spec :: Spec +spec = do + let battery :: Stream Battery + battery = extern "battery" Nothing + + -- Check equality, indexing into nested structs and arrays. Note that this is + -- trivial by equality. + void $ prop "Example 1" $ forAll $ + (((battery#volts) ! 0)#numVolts) == (((battery#volts) ! 0)#numVolts) + + -- Same as previous example, but get a different array index (so should be + -- false). + void $ prop "Example 2" $ forAll $ + (((battery#other) ! 2) ! 3) == (((battery#other) ! 2) ! 4) + + -- Same as previous example, but in trigger form + trigger "otherTrig" ((((battery#other) ! 2) ! 3) == (((battery#other) ! 2) ! 4)) + [arg battery] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + + -- Use Z3 to prove the properties. + results <- prove Z3 spec' + + -- Print the results. + when (verb P.>= Default) $ + forM_ results $ \(nm, res) -> do + putStr $ nm <> ": " + case res of + Valid -> putStrLn "valid" + Invalid -> putStrLn "invalid" + Unknown -> putStrLn "unknown" + + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "structs" spec' diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateArray.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateArray.hs new file mode 100644 index 000000000..55a43b1ab --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateArray.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE RebindableSyntax #-} +{-# LANGUAGE DataKinds #-} + +-- | An example showing of using @copilot-verifier@ to verify a specification +-- involving arrays where individual elements are updated. +module Copilot.Verifier.Examples.ShouldPass.UpdateArray where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +spec :: Spec +spec = do + let pair :: Stream (Array 2 Word32) + pair = extern "pair" Nothing + + -- Check equality, indexing into array and modifying the value. Note that + -- this is trivial by equality. + trigger "trig_1" + (((pair !! 0 =$ (+1)) ! 0) == ((pair ! 0) + 1)) + [arg pair] + + -- Same as previous example, but get a different array index (so should be + -- false). + trigger "trig_2" + (((pair !! 0 =$ (+1)) ! 1) == ((pair ! 0) + 1)) + [arg pair] + +verifySpec :: Verbosity -> IO () +verifySpec verb = reify spec >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "updateArray" diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateStruct.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateStruct.hs new file mode 100644 index 000000000..ecf3f4bdd --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/UpdateStruct.hs @@ -0,0 +1,62 @@ +{-# LANGUAGE RebindableSyntax #-} +{-# LANGUAGE DataKinds #-} + +-- | An example showing of using @copilot-verifier@ to verify a specification +-- involving structs where individual fields are updated. +module Copilot.Verifier.Examples.ShouldPass.UpdateStruct where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +-- | Definition for `Volts`. +data Volts = Volts + { numVolts :: Field "numVolts" Word32 + , flag :: Field "flag" Bool + } + +-- | `Struct` instance for `Volts`. +instance Struct Volts where + typeName _ = "volts" + toValues vlts = [ Value Word32 (numVolts vlts) + , Value Bool (flag vlts) + ] + +-- | `Volts` instance for `Typed`. +instance Typed Volts where + typeOf = Struct (Volts (Field 0) (Field False)) + +data Battery = Battery + { temp :: Field "temp" Word32 + , volts :: Field "volts" (Array 10 Volts) + , other :: Field "other" (Array 10 (Array 5 Word32)) + } + +-- | `Battery` instance for `Struct`. +instance Struct Battery where + typeName _ = "battery" + toValues battery = [ Value typeOf (temp battery) + , Value typeOf (volts battery) + , Value typeOf (other battery) + ] + +-- | `Battery` instance for `Typed`. Note that `undefined` is used as an +-- argument to `Field`. This argument is never used, so `undefined` will never +-- throw an error. +instance Typed Battery where + typeOf = Struct (Battery (Field 0) (Field undefined) (Field undefined)) + +spec :: Spec +spec = do + let battery :: Stream Battery + battery = extern "battery" Nothing + + -- Update a struct field, then check it for equality. + trigger "updateTrig" + ((battery ## temp =$ (+1))#temp == (battery#temp + 1)) + [arg battery] + +verifySpec :: Verbosity -> IO () +verifySpec verb = reify spec >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "updateStruct" diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Voting.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Voting.hs new file mode 100644 index 000000000..46562bdb1 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/Voting.hs @@ -0,0 +1,64 @@ +-------------------------------------------------------------------------------- +-- Copyright © 2019 National Institute of Aerospace / Galois, Inc. +-------------------------------------------------------------------------------- + +-- | Fault-tolerant voting examples. + +{-# LANGUAGE RebindableSyntax #-} + +module Copilot.Verifier.Examples.ShouldPass.Voting where + +import Language.Copilot +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +vote :: Spec +vote = do + -- majority selects element with the biggest occurance. + trigger "maj" true [arg maj] + + -- aMajority checks if the selected element has a majority. + trigger "aMaj" true [arg $ aMajority inputs maj] + + where + maj = majority inputs + + -- 26 input streams to vote on + inputs :: [Stream Word32] + inputs = [ a, b, c, d, e, f, g, h, i, j, k, l, m + -- , n, o, p, q, r, s, t, u, v, w, x, y, z + ] + a = [0] ++ a + 1 + b = [0] ++ b + 1 + c = [0] ++ c + 1 + d = [0] ++ d + 1 + e = [1] ++ e + 1 + f = [1] ++ f + 1 + g = [1] ++ g + 1 + h = [1] ++ h + 1 + i = [1] ++ i + 1 + j = [1] ++ j + 1 + k = [1] ++ k + 1 + l = [1] ++ l + 1 + m = [1] ++ m + 1 +{- + n = [1] ++ n + 1 + o = [1] ++ o + 1 + p = [1] ++ p + 1 + q = [1] ++ q + 1 + r = [1] ++ r + 1 + s = [1] ++ s + 1 + t = [1] ++ t + 1 + u = [1] ++ u + 1 + v = [1] ++ v + 1 + w = [1] ++ w + 1 + x = [1] ++ x + 1 + y = [1] ++ y + 1 + z = [1] ++ z + 1 +-} + +verifySpec :: Verbosity -> IO () +--verifySpec _ = interpret 30 vote +verifySpec verb = reify vote >>= verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "voting" diff --git a/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/WCV.hs b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/WCV.hs new file mode 100644 index 000000000..118e7a386 --- /dev/null +++ b/copilot-verifier/examples/Copilot/Verifier/Examples/ShouldPass/WCV.hs @@ -0,0 +1,189 @@ +-- | This example shows an implementation of the Well-Clear Violation +-- algorithm, it follows the implementation described in 'Analysis of +-- Well-Clear Bounday Models for the Integration of UAS in the NAS', +-- https://ntrs.nasa.gov/citations/20140010078. + +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE RebindableSyntax #-} + +module Copilot.Verifier.Examples.ShouldPass.WCV where + +import Language.Copilot +-- import qualified Copilot.Theorem.What4 as CT +import Copilot.Compile.C99 +import Copilot.Verifier ( Verbosity, VerifierOptions(..) + , defaultVerifierOptions, verifyWithOptions ) + +-- import Data.Foldable (forM_) +import qualified Control.Monad as Monad + + +-- | `dthr` is the horizontal distance threshold. +dthr :: Stream Double +dthr = extern "dthr" Nothing + +-- | `tthr` is the horizontal time threshold. +tthr :: Stream Double +tthr = extern "tthr" Nothing + +-- | `zthr` is the vertical distance / altitude threshold. +zthr :: Stream Double +zthr = extern "zthr" Nothing + +-- | `tcoathr` is the vertical time threshold. +tcoathr :: Stream Double +tcoathr = extern "tcoathr" Nothing + +type Vect2 = (Stream Double, Stream Double) + + +------------------ +-- The following section contains basic libraries for working with vectors. +------------------ + +-- | Multiply two Vectors. +(|*|) :: Vect2 -> Vect2 -> Stream Double +(|*|) (x1, y1) (x2, y2) = (x1 * x2) + (y1 * y2) + +-- | Calculate the square of a vector. +sq :: Vect2 -> Stream Double +sq x = x |*| x + +-- | Calculate the length of a vector. +norm :: Vect2 -> Stream Double +norm = sqrt . sq + +-- | Calculate the determinant of two vectors. +det :: Vect2 -> Vect2 -> Stream Double +det (x1, y1) (x2, y2) = (x1 * y2) - (x2 * y1) + +-- | Compare two vectors, taking into account the small error that is +-- introduced by the usage of `Double`s. +(~=) :: Stream Double -> Stream Double -> Stream Bool +a ~= b = (abs (a - b)) < 0.001 + +-- | Negate a vector. +neg :: Vect2 -> Vect2 +neg (x, y) = (negate x, negate y) + + +-------------------- +-- From here on the algorithm, as described by the paper mentioned on the top +-- of this file, is implemented. Please refer to the paper for details. +-------------------- + +tau :: Vect2 -> Vect2 -> Stream Double +tau s v = if s |*| v < 0 + then (-(sq s)) / (s |*| v) + else -1 + +tcpa :: Vect2 -> Vect2 -> Stream Double +tcpa s v@(vx, vy) = if vx ~= 0 && vy ~= 0 + then 0 + else -(s |*| v)/(sq v) + +taumod :: Vect2 -> Vect2 -> Stream Double +taumod s v = if s |*| v < 0 + then (dthr * dthr - (sq s))/(s |*| v) + else -1 + +tep :: Vect2 -> Vect2 -> Stream Double +tep s v = if (s |*| v < 0) && ((delta s v dthr) >= 0) + then theta s v dthr (-1) + else -1 + +delta :: Vect2 -> Vect2 -> Stream Double -> Stream Double +delta s v d = (d*d) * (sq v) - ((det s v)*(det s v)) +-- Here the formula says : (s . orth v)^2 which is the same as det(s,v)^2 + +theta :: Vect2 -> Vect2 -> Stream Double -> Stream Double -> Stream Double +theta s v d e = (-(s |*| v) + e * (sqrt $ delta s v d)) / (sq v) + + +tcoa :: Stream Double -> Stream Double -> Stream Double +tcoa sz vz = if (sz * vz) < 0 + then (-sz) / vz + else -1 + +dcpa :: Vect2 -> Vect2 -> Stream Double +dcpa s@(sx, sy) v@(vx, vy) = norm (sx + (tcpa s v) * vx, sy + (tcpa s v) * vy) + + +-------------------------- +-- Well clear Violation -- +-------------------------- + +-- | Determines if the well clear property is violated or not. +wcv :: (Vect2 -> Vect2 -> Stream Double) -> + Vect2 -> Stream Double -> + Vect2 -> Stream Double -> + Stream Bool +wcv tvar s sz v vz = (horizontalWCV tvar s v) && (verticalWCV sz vz) + +verticalWCV :: Stream Double -> Stream Double -> Stream Bool +verticalWCV sz vz = + ((abs $ sz) <= zthr) || + (0 <= (tcoa sz vz) && (tcoa sz vz) <= tcoathr) + +horizontalWCV :: (Vect2 -> Vect2 -> Stream Double) -> Vect2 -> Vect2 -> Stream Bool +horizontalWCV tvar s v = + (norm s <= dthr) || + (((dcpa s v) <= dthr) && (0 <= (tvar s v)) && ((tvar s v) <= tthr)) + +spec :: Spec +spec = do + -- External streams for relative position and velocity. + let -- The relative x velocity between ownship and the intruder. + vx :: Stream Double + vx = extern "relative_velocity_x" Nothing + + -- The relative y velocity between ownship and the intruder. + vy :: Stream Double + vy = extern "relative_velocity_y" Nothing + + -- The relative z velocity between ownship and the intruder. + vz :: Stream Double + vz = extern "relative_velocity_z" Nothing + + -- The relative velocity as a 2D vector. + v :: (Stream Double, Stream Double) + v = (vx, vy) + + + -- The relative x position between ownship and the intruder. + sx :: Stream Double + sx = extern "relative_position_x" Nothing + + -- The relative y position between ownship and the intruder. + sy :: Stream Double + sy = extern "relative_position_y" Nothing + + -- The relative z position between ownship and the intruder. + sz :: Stream Double + sz = extern "relative_position_z" Nothing + + -- The relative position as a 2D vector. + s :: (Stream Double, Stream Double) + s = (sx, sy) + Monad.void $ prop "1a" (forAll $ (tau s v) ~= (tau (neg s) (neg v))) + -- Monad.void $ prop "3d" (forAll $ (wcv tep s sz v vz) == (wcv tep (neg s) (-sz) (neg v) (-vz))) + trigger "well_clear_violation" (wcv tep s sz v vz) [] + +verifySpec :: Verbosity -> IO () +verifySpec verb = do + spec' <- reify spec + verifyWithOptions defaultVerifierOptions{verbosity = verb} + mkDefaultCSettings [] "wcv" spec' + +{- + -- Use Z3 to prove the properties. + results <- CT.prove CT.Z3 spec' + + -- Print the results. + forM_ results $ \(nm, res) -> do + putStr $ nm <> ": " + case res of + CT.Valid -> putStrLn "valid" + CT.Invalid -> putStrLn "invalid" + CT.Unknown -> putStrLn "unknown" +-} diff --git a/copilot-verifier/exe/VerifyExamples.hs b/copilot-verifier/exe/VerifyExamples.hs new file mode 100644 index 000000000..9fad16b53 --- /dev/null +++ b/copilot-verifier/exe/VerifyExamples.hs @@ -0,0 +1,53 @@ +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +module Main (main) where + +import qualified Data.CaseInsensitive as CI +import Data.CaseInsensitive (CI) +import Data.Foldable (for_) +import qualified Data.Map as Map +import qualified Data.Text as Text +import qualified Data.Text.IO as Text +import Data.Text (Text) +import Data.Traversable (for) +import Options.Applicative + +import Copilot.Verifier (Verbosity(..)) +import Copilot.Verifier.Examples (shouldFailExamples, shouldPassExamples) + +newtype Options = Options + { examples :: [CI Text] + } deriving Show + +optsParser :: Parser Options +optsParser = Options + <$> (some . strArgument) + ( metavar "EXAMPLE1 [EXAMPLE2 ...]" + <> help "The names of the examples to run" ) + +main :: IO () +main = execParser opts >>= verifyExamples + where + opts = info (optsParser <**> helper) + ( fullDesc + <> header "Run one or more copilot-verifier examples" + <> progDesc (unlines [ "Run one or more examples from the copilot-verifier/examples directory." + , "Each EXAMPLE must correspond to an example name." + ])) + +verifyExamples :: Options -> IO () +verifyExamples Options{examples} = do + -- Check that all requested examples exist + examplesWithMain <- for examples $ \example -> + case Map.lookup example (shouldFailExamples Default `Map.union` shouldPassExamples Default) of + Just m -> pure (example, m) + Nothing -> fail $ "No example named " ++ Text.unpack (CI.original example) + + -- Run the examples + for_ examplesWithMain $ \(example, exampleMain) -> do + Text.putStrLn "=====" + Text.putStrLn $ "== Running the " <> CI.original example <> " example..." + Text.putStrLn "=====" + Text.putStrLn "" + exampleMain + Text.putStrLn "" diff --git a/copilot-verifier/src/Copilot/Verifier.hs b/copilot-verifier/src/Copilot/Verifier.hs new file mode 100644 index 000000000..cd0f3d877 --- /dev/null +++ b/copilot-verifier/src/Copilot/Verifier.hs @@ -0,0 +1,1607 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Copilot.Verifier + ( verify + , verifyWithOptions + , VerifierOptions(..) + , defaultVerifierOptions + , sideCondVerifierOptions + , Verbosity(..) + ) where + +import Control.Lens (view, (^.), to) +import Control.Monad (foldM, forM_, unless, when) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.State (execStateT, lift, StateT(..)) +import Data.Aeson (ToJSON) +import Data.Foldable (traverse_) +import Data.Functor (void) +import qualified Data.Text as Text +import qualified Data.Map.Strict as Map +import Data.IORef (newIORef, modifyIORef', readIORef, IORef) +import qualified Text.LLVM.AST as L +import Data.List (genericLength, sort) +import Data.List.NonEmpty (NonEmpty(..)) +import qualified Data.List.NonEmpty as NE +import qualified Data.Vector as V +import qualified Data.BitVector.Sized as BV +import GHC.Generics (Generic) +import qualified Prettyprinter as PP +import System.Exit (exitFailure) +import System.FilePath ((), (<.>)) + +import Copilot.Compile.C99 (CSettings(..), compileWith) +import Copilot.Core +import qualified Copilot.Core.Type as CT + +import qualified Copilot.Theorem.What4 as CW4 + +import qualified Copilot.Verifier.FloatMode as FloatMode +import qualified Copilot.Verifier.Log as Log +import qualified Copilot.Verifier.Solver as Solver + +import Data.Parameterized.Ctx (EmptyCtx) +import Data.Parameterized.Context (pattern Empty) +import qualified Data.Parameterized.Context as Ctx +import Data.Parameterized.NatRepr (intValue, natValue, testEquality, knownNat, type (<=) ) +import Data.Parameterized.Nonce (globalNonceGenerator) +import Data.Parameterized.Some (Some(..)) +import Data.Parameterized.TraversableFC (toListFC) +import Data.Parameterized.TraversableFC.WithIndex (ifoldlMFC) +import qualified Data.Parameterized.Vector as PVec + +import Lang.Crucible.Backend + ( IsSymInterface, Goals(..), Assumptions, Assertion + , pushAssumptionFrame, popUntilAssumptionFrame + , getProofObligations, clearProofObligations + , LabeledPred(..), abortExecBecause, AbortExecReason(..), addAssumption + , addDurableAssertion, addDurableProofObligation + , CrucibleAssumption(..), ppAbortExecReason + , IsSymBackend(..), HasSymInterface(..) + , labeledPred, labeledPredMsg + -- , ProofObligations, proofGoal, goalsToList, labeledPredMsg + ) +import Lang.Crucible.Backend.Simple (SimpleBackend, newSimpleBackend) +import Lang.Crucible.CFG.Core (AnyCFG(..), cfgArgTypes, cfgReturnType) +import Lang.Crucible.CFG.Common ( freshGlobalVar ) +import Lang.Crucible.FunctionHandle (HandleAllocator, newHandleAllocator) +import Lang.Crucible.Simulator + ( SimContext(..), ctxSymInterface, ExecResult(..), ExecState(..) + , defaultAbortHandler, runOverrideSim, partialValue, gpValue + , GlobalVar, executeCrucible, OverrideSim, ovrWithBackend, regValue + , readGlobal, modifyGlobal, callCFG, emptyRegMap, RegEntry(..) + , AbortedResult(..) + ) +import Lang.Crucible.Simulator.ExecutionTree ( withBackend ) +import Lang.Crucible.Simulator.GlobalState ( insertGlobal ) +import Lang.Crucible.Simulator.RegValue (RegValue, RegValue'(..)) +import Lang.Crucible.Simulator.SimError (SimError(..), SimErrorReason(..)) -- ppSimError +import Lang.Crucible.Types + ( TypeRepr(..), (:~:)(..), KnownRepr(..), NatType ) + +import Lang.Crucible.LLVM (llvmGlobals, registerLazyModule, register_llvm_overrides) +import Lang.Crucible.LLVM.Bytes (bitsToBytes) +import Lang.Crucible.LLVM.DataLayout (Alignment, DataLayout) +import Lang.Crucible.LLVM.Errors (BadBehavior) +import Lang.Crucible.LLVM.Extension (LLVM, ArchWidth) +import Lang.Crucible.LLVM.Globals (initializeAllMemory, populateAllGlobals) +import Lang.Crucible.LLVM.Intrinsics + ( IntrinsicsOptions, OverrideTemplate, basic_llvm_override, LLVMOverride(..) ) + +import Lang.Crucible.LLVM.MemType + ( MemType(..), SymType(..) + , i1, i8, i16, i32, i64 + , memTypeSize, memTypeAlign + , mkStructInfo + ) +import Lang.Crucible.LLVM.MemModel + ( mkMemVar, withPtrWidth, HasLLVMAnn, LLVMAnnMap, MemImpl + , HasPtrWidth, doResolveGlobal, doStore + , LLVMPtr, LLVMVal, MemOptions, PartLLVMVal(..), StorageType, bitvectorType + , ptrAdd, toStorableType, projectLLVM_bv + , pattern LLVMPointerRepr, pattern PtrRepr, loadRaw, llvmPointer_bv + , memRepr, Mem, unpackMemValue + ) +import Lang.Crucible.LLVM.MemModel.CallStack (CallStack) +import Lang.Crucible.LLVM.MemModel.Partial (BoolAnn(..)) +import Lang.Crucible.LLVM.PrettyPrint (ppSymbol) +import Lang.Crucible.LLVM.Translation + ( LLVMTranslationWarning(..), ModuleTranslation + , getTranslatedCFG, translateModule, globalInitMap + , transContext, llvmPtrWidth, llvmTypeCtx, llvmTypeAsRepr + ) +import Lang.Crucible.LLVM.TypeContext (TypeContext, llvmDataLayout) + +import Crux (defaultOutputConfig) +import Crux.Config (cfgJoin, Config(..)) +import Crux.Config.Load (fromFile, fromEnv) +import Crux.Config.Common + ( cruxOptions, CruxOptions(..), postprocessOptions, outputOptions + , OutputOptions(..) + ) +import Crux.Goal (proveGoalsOffline, provedGoalsTree) +import qualified Crux.Log as Log +import Crux.Types (SimCtxt, Crux, ProcessedGoals(..), ProofResult(..)) + +import Crux.LLVM.Config (llvmCruxConfig, LLVMOptions(..)) +import Crux.LLVM.Compile (genBitCode) +import qualified Crux.LLVM.Log as Log +import Crux.LLVM.Simulate (setupSimCtxt, parseLLVM, explainFailure) +import CruxLLVMMain (processLLVMOptions) + +import What4.Config + (extendConfig) +import What4.Interface + ( Pred, bvLit, bvAdd, bvUrem, bvMul, bvIsNonzero, bvEq, isEq + , getConfiguration, freshBoundedBV, predToBV + , getCurrentProgramLoc, printSymExpr + , truePred, falsePred, andPred, annotateTerm, backendPred + , getAnnotation, natAdd, natEq, natIte, natLit + ) +import What4.Expr.Builder + ( Flags, ExprBuilder, BoolExpr, startCaching + , newExprBuilder + ) +import What4.FunctionName (functionName) +import What4.InterpretedFloatingPoint + ( FloatInfoRepr(..), IsInterpretedFloatExprBuilder(..) + , SingleFloat, DoubleFloat + ) +import What4.ProgramLoc (ProgramLoc, mkProgramLoc, plFunction, Position(..)) +import What4.Solver.Adapter (SolverAdapter(..)) +import What4.Symbol (safeSymbol) + +-- | @'verify' csettings props prefix spec@ verifies the Copilot specification +-- @spec@ under the assumptions @props@ matches the behavior of the C program +-- compiled with @csettings@ within a directory prefixed by @prefix@. +verify :: CSettings -> [String] -> String -> Spec -> IO () +verify = verifyWithOptions defaultVerifierOptions + +-- | Options for configuring the behavior of the verifier. +data VerifierOptions = VerifierOptions + { verbosity :: Verbosity + -- ^ How much output the verifier should produce. + , assumePartialSideConds :: Bool + -- ^ If 'True', the verifier will determine the conditions under which + -- a Copilot specification's partial operations are well defined and + -- add these side conditions as assumptions. As a result, even if the + -- generated C code performs a partial operation, the verification will + -- succeed if this partial operation coincides with a corresponding + -- operation on the Copilot side. + -- + -- If 'False', the verifier will not assume any side conditions related + -- to partial operations in the Copilot specification. As a result, any + -- use of a partial operation in the generated C code will cause + -- verification to fail unless the user adds their own assumptions. + , logSmtInteractions :: Bool + -- ^ If 'True', create log files corresponding to the SMT solver + -- interactions used to discharge each proof goal. The file will be named + -- @--.smt2@, where: + -- + -- * @@ will be either @initial-step@ or @transition-step@, depending + -- on which step of the proof the goal corresponds to. + -- + -- * @@ will be the number of the goal, starting at 0 and + -- counting up. Note that each step of the proof has its own goal + -- numbers. This means that there can be both an + -- @initial-step-0-.smt2@ and a @transition-step-0-.smt2@, + -- and similarly for other numbers. + -- + -- * @@ is the name of the SMT solver used to discharge the proof + -- goal. Currently, this will always be @z3@, although we might make this + -- configurable in the future. + , smtSolver :: Solver.Solver + -- ^ Which SMT solver to use when solving proof goals. + , smtFloatMode :: FloatMode.FloatMode + -- ^ How the verifier should interpret floating-point operations when + -- translating them to SMT. + -- + -- By default, the verifier will treat all floating-point operations as + -- uninterpreted functions ('FloatMode.FloatUninterpreted'). This allows the + -- verifier to perform some limited reasoning about floating-point + -- operations that SMT solvers do not have built-in operations for (@sin@, + -- @cos@, @tan@, etc.), but at the expense of not being able to verify C + -- code in which the compiler optimizes floating-point expressions. One can + -- also opt into an alternative mode where floating-point values are treated + -- as IEEE-754 floats ('FloatMode.FloatIEEE'), but this comes with the + -- drawback that the verifier will not be able to perform /any/ reasoning + -- about @sin@, @cos@, @tan@, etc. + } deriving stock Show + +-- | The default 'VerifierOptions': +-- +-- * Produce a reasonable amount of diagnostics as verification proceeds +-- ('Default'). +-- +-- * Do not assume any side conditions related to partial operations. +-- +-- * Do not log any SMT solver interactions. +-- +-- * Use the Z3 SMT solver. +-- +-- * Treat all floating-point operations as uninterpreted functions when +-- translating to SMT. +defaultVerifierOptions :: VerifierOptions +defaultVerifierOptions = VerifierOptions + { verbosity = Default + , assumePartialSideConds = False + , logSmtInteractions = False + , smtSolver = Solver.Z3 + , smtFloatMode = FloatMode.FloatUninterpreted + } + +-- | Like 'defaultVerifierOptions', except that the verifier will assume side +-- conditions related to partial operations used in the Copilot spec. +sideCondVerifierOptions :: VerifierOptions +sideCondVerifierOptions = defaultVerifierOptions + { assumePartialSideConds = True + } + +-- | How much output should verification produce? +-- +-- The data constructors are listed in increasing order of how many diagnostics +-- they produce. +data Verbosity + = Quiet -- ^ Don't produce any diagnostics. + | Default -- ^ Produce a reasonable amount of diagnostics as verification proceeds. + | Noisy -- ^ Produce as many diagnostics as possible. + deriving stock (Eq, Ord, Show) + +-- | Like 'verify', but with 'VerifierOptions' to more finely control the +-- verifier's behavior. +verifyWithOptions :: VerifierOptions -> CSettings -> [String] -> String -> Spec -> IO () +verifyWithOptions opts csettings0 properties prefix spec = + withCopilotLogging $ + do -- munge options structures into the necessary forms + (ocfg, cruxOpts, llvmOpts, csettings, csrc) <- computeConfiguration opts csettings0 prefix + let ?outputConfig = ocfg + + -- Compile the Copilot spec into C source code, using + -- preexisting Copilot library calls. + compileWith csettings prefix spec + Log.sayCopilot $ Log.GeneratedCFile csrc + + -- Compile the C source into LLVM bitcode, using preexisting + -- Crux library calls. + bcFile <- genBitCode cruxOpts llvmOpts + Log.sayCopilot $ Log.CompiledBitcodeFile prefix bcFile + + -- Run the main verification procedure + verifyBitcode opts csettings properties spec cruxOpts llvmOpts csrc bcFile + + +-- | Do the (surprisingly large amount) of options munging necessary to set up +-- the crucible/crux environment. +computeConfiguration :: + Log.SupportsCruxLogMessage CopilotLogging => + VerifierOptions -> CSettings -> FilePath -> + IO (Log.OutputConfig CopilotLogging, CruxOptions, LLVMOptions, CSettings, FilePath) +computeConfiguration opts csettings0 prefix = + do ocfg1 <- defaultOutputConfig copilotLoggingToSayWhat + let quiet = verbosity opts == Quiet + let ocfg2 mbOutputOpts = (ocfg1 mbOutputOpts) { Log._quiet = quiet } + llvmcfg <- llvmCruxConfig + let cfg = cfgJoin cruxOptions llvmcfg + -- TODO, load from an actual configuration file? + fileOpts <- fromFile "copilot-verifier" cfg Nothing + (cruxOpts0, llvmOpts0) <- foldM fromEnv fileOpts (cfgEnv cfg) + let odir0 = cSettingsOutputDirectory csettings0 + let odir = -- A bit grimy, but this corresponds to how crux-llvm sets + -- its output directory. + if odir0 == "." + then "results" prefix + else odir0 + let csettings = csettings0{ cSettingsOutputDirectory = odir } + let csrc = odir prefix ++ ".c" + let cruxOpts1 = cruxOpts0{ outDir = odir, bldDir = odir, inputFiles = [csrc] + , outputOptions = + (outputOptions cruxOpts0) + { quietMode = quiet + , simVerbose = if verbosity opts > Default + then 2 + else 0 + } + } + let ?outputConfig = ocfg2 (Just (outputOptions cruxOpts1)) + cruxOpts2 <- postprocessOptions cruxOpts1 + + -- Tweak the options passed to Clang: + -- + -- - Fix the optimization level to -O0. + -- + -- - Pass -ffp-contract=off to prevent sequences of floating-point + -- multiplications/additions from being optimized to llvm.fmuladd + -- intrinsics, which makes floating-point verification fragile. + let llvmOpts1 = llvmOpts0 + { optLevel = 0 + , clangOpts = "-ffp-contract=off" : clangOpts llvmOpts0 + } + (cruxOpts3, llvmOpts2) <- processLLVMOptions (cruxOpts2, llvmOpts1) + + let ocfg3 = ocfg2 (Just (outputOptions cruxOpts3)) + return (ocfg3, cruxOpts3, llvmOpts2, csettings, csrc) + + +data CopilotVerifierData t = CopilotVerifierData + + +-- | Main entry point for the verifier. +verifyBitcode :: + Log.Logs msgs => + Log.SupportsCruxLogMessage msgs => + Log.SupportsCruxLLVMLogMessage msgs => + Log.SupportsCopilotLogMessage msgs => + VerifierOptions {- ^ Verifier-specific settings -} -> + CSettings {- ^ Settings used to compile the Copilot spec. Used to find the names of functions and variables. -} -> + [String] {- ^ Names of properties to assume during verification. -} -> + Spec {- ^ The input Copilot specification -} -> + CruxOptions {- ^ Crux options -} -> + LLVMOptions {- ^ CruxLLVM options -} -> + FilePath {- ^ Path to the generated C file (for logging purposes only) -} -> + FilePath {- ^ Path to the bitcode file to verify -} -> + IO () +verifyBitcode opts csettings properties spec cruxOpts llvmOpts cFile bcFile = + FloatMode.withFloatMode (smtFloatMode opts) $ \fm -> + do -- Set up the expression builder and symbolic backend + halloc <- newHandleAllocator + sym <- newExprBuilder fm CopilotVerifierData globalNonceGenerator + bak <- newSimpleBackend sym + -- turn on hash-consing + startCaching sym + + FloatMode.withInterpretedFloatExprBuilder sym fm $ + verifyWithSymBackend bak halloc + where + verifyWithSymBackend :: + forall t st fm. + IsInterpretedFloatExprBuilder (ExprBuilder t st (Flags fm)) => + SimpleBackend t st (Flags fm) -> + HandleAllocator -> + IO () + verifyWithSymBackend bak halloc = do + let sym = backendGetSym bak + -- capture LLVM side-condition information for use in error reporting + clRefs <- newCopilotLogRefs + let ?recordLLVMAnnotation = recordLLVMAnnotation clRefs + + -- Set up the solver to use for verification. + let adapter :: SolverAdapter st + adapter = Solver.solverAdapter (smtSolver opts) + extendConfig (solver_adapter_config_options adapter) (getConfiguration sym) + + -- Set up the Crucible/LLVM simulation context + memVar <- mkMemVar "llvm_memory" halloc + let simctx = (setupSimCtxt halloc bak (memOpts llvmOpts) memVar) + { printHandle = view Log.outputHandle ?outputConfig } + + -- Load and translate the input LLVM module + llvmMod <- parseLLVM bcFile + Some trans <- + let ?transOpts = transOpts llvmOpts + in translateModule halloc memVar llvmMod + + Log.sayCopilot Log.TranslatedToCrucible + + -- Grab some metadata from the bitcode file and options; + -- make the available via implicit arguments to the places + -- that expect them. + let llvmCtxt = trans ^. transContext + let ?lc = llvmCtxt ^. llvmTypeCtx + let ?memOpts = memOpts llvmOpts + let ?intrinsicsOpts = intrinsicsOpts llvmOpts + + llvmPtrWidth llvmCtxt $ \ptrW -> + withPtrWidth ptrW $ + + do -- Compute the LLVM memory state with global variables allocated + -- but not initialized + emptyMem <- initializeAllMemory bak llvmCtxt llvmMod + + -- Compute the LLVM memory state with global variables initialized + -- to their initial values. + initialMem <- populateAllGlobals bak (trans ^. globalInitMap) emptyMem + + -- Use the Copilot spec directly to compute the symbolic states + -- necessary to carry out the states of the bisimulation proof. + Log.sayCopilot Log.GeneratingProofState + proofStateBundle <- CW4.computeBisimulationProofBundle sym properties spec + + -- First check that the initial state of the program matches the starting + -- segment of the associated Copilot streams. + let cruxOptsInit = setCruxOfflineSolverOutput "initial-step" cruxOpts + initGoals <- + verifyInitialState cruxOptsInit [adapter] clRefs simctx initialMem + (CW4.initialStreamState proofStateBundle) + + -- Now, the real meat. Carry out the bisimulation step of the proof. + let cruxOptsTrans = setCruxOfflineSolverOutput "transition-step" cruxOpts + bisimGoals <- + verifyStepBisimulation opts cruxOptsTrans [adapter] csettings + clRefs simctx llvmMod trans memVar initialMem proofStateBundle + + Log.sayCopilot $ Log.SuccessfulProofSummary cFile initGoals bisimGoals + -- We only want to inform users about Noisy if the verbosity level is + -- sufficiently low. Crux's logging framework isn't particularly + -- suited to doing this, as it assumes that all log messages enabled + -- for low verbosity levels should also be enabled for higher + -- verbosity levels. That is a reasonable assumption most of the time, + -- but not here. + -- + -- To compensate, we hack around the issue by manually checking the + -- verbosity level before logging the message. + when (verbosity opts < Noisy) $ + Log.sayCopilot Log.NoisyVerbositySuggestion + + -- If @logSmtInteractions@ is enabled, enable offline solver output in the + -- supplied 'CruxOptions' with the supplied file template. Otherwise, return + -- the supplied 'CruxOptions' unaltered. + setCruxOfflineSolverOutput :: FilePath -> CruxOptions -> CruxOptions + setCruxOfflineSolverOutput template cruxOpts' + | logSmtInteractions opts + = cruxOpts' + { offlineSolverOutput = Just $ outDir cruxOpts' template <.> "smt2" } + | otherwise + = cruxOpts' + +-- | Capture LLVM side-condition information for use in error reporting. +recordLLVMAnnotation :: + IsSymInterface sym => + CopilotLogRefs sym -> + CallStack -> + BoolAnn sym -> + BadBehavior sym -> + IO () +recordLLVMAnnotation clRefs stk bann bb = + modifyIORef' (llvmAnnMapRef clRefs) (Map.insert bann (stk, bb)) + +-- | Prove that the state of the global variables at program startup +-- matches the expected initial segments of the associated Copilot +-- streams. +verifyInitialState :: + IsSymInterface sym => + Log.Logs msgs => + Log.SupportsCruxLogMessage msgs => + Log.SupportsCopilotLogMessage msgs => + sym ~ ExprBuilder t st fs => + HasPtrWidth wptr => + HasLLVMAnn sym => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + + CruxOptions -> + [SolverAdapter st] -> + CopilotLogRefs sym -> + SimCtxt Crux sym LLVM -> + MemImpl sym -> + CW4.BisimulationProofState sym -> + IO Integer +verifyInitialState cruxOpts adapters clRefs simctx mem initialState = + withBackend simctx $ \bak -> + do Log.sayCopilot $ Log.ComputingConditions Log.InitialState + frm <- pushAssumptionFrame bak + + assertStateRelation bak clRefs mem + Log.InitialStateRelation initialState + + popUntilAssumptionFrame bak frm + + Log.sayCopilot $ Log.ProvingConditions Log.InitialState + proveObls cruxOpts adapters clRefs Log.InitialState simctx + + +verifyStepBisimulation :: + IsSymInterface sym => + Log.Logs msgs => + Log.SupportsCruxLogMessage msgs => + Log.SupportsCruxLLVMLogMessage msgs => + Log.SupportsCopilotLogMessage msgs => + sym ~ ExprBuilder t st fs => + HasPtrWidth wptr => + HasLLVMAnn sym => + (1 <= ArchWidth arch) => + HasPtrWidth (ArchWidth arch) => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + (?intrinsicsOpts :: IntrinsicsOptions) => + + VerifierOptions -> + CruxOptions -> + [SolverAdapter st] -> + CSettings -> + CopilotLogRefs sym -> + SimCtxt Crux sym LLVM -> + L.Module -> + ModuleTranslation arch -> + GlobalVar Mem -> + MemImpl sym -> + CW4.BisimulationProofBundle sym -> + IO Integer +verifyStepBisimulation opts cruxOpts adapters csettings clRefs simctx llvmMod modTrans memVar mem prfbundle = + withBackend simctx $ \bak -> + do Log.sayCopilot $ Log.ComputingConditions Log.StepBisimulation + + frm <- pushAssumptionFrame bak + + do -- set up the memory image + mem' <- setupPrestate bak mem prfbundle + + -- sanity check, verify that we set up the memory in the expected relation + assertStateRelation bak clRefs mem' + Log.PreStepStateRelation (CW4.preStreamState prfbundle) + + -- set up trigger guard global variables + let halloc = simHandleAllocator simctx + -- See Note [Global variables for trigger functions] + let prepTrigger (nm, guard, _) = + do gv <- freshGlobalVar halloc (Text.pack (nm ++ "_called")) NatRepr + return (nm, gv, guard) + + checkDuplicateTriggerNames :: [Name] -> IO () + checkDuplicateTriggerNames triggers = + traverse_ checkDuplicateTriggerName $ NE.group $ sort triggers + + checkDuplicateTriggerName :: NonEmpty Name -> IO () + checkDuplicateTriggerName (trig :| dupTrigs) = + unless (null dupTrigs) $ + fail $ unlines + [ "The specification invokes the `" ++ trig ++ + "` trigger function multiple times," + , "which copilot-verifier does not currently support." + , "See https://github.com/Copilot-Language/copilot-verifier/issues/74." + ] + let triggerState = CW4.triggerState prfbundle + checkDuplicateTriggerNames $ map (\(nm,_,_) -> nm) triggerState + triggerGlobals <- mapM prepTrigger triggerState + + -- execute the step function + let overrides = zipWith (triggerOverride clRefs) triggerGlobals triggerState + mem'' <- executeStep opts csettings clRefs simctx memVar mem' llvmMod modTrans triggerGlobals overrides (CW4.assumptions prfbundle) (CW4.sideConds prfbundle) + + -- assert the poststate is in the relation + assertStateRelation bak clRefs mem'' + Log.PostStepStateRelation (CW4.postStreamState prfbundle) + + popUntilAssumptionFrame bak frm + + Log.sayCopilot $ Log.ProvingConditions Log.StepBisimulation + proveObls cruxOpts adapters clRefs Log.StepBisimulation simctx + + +-- | Set up the "trigger override" functions. These dummy functions +-- take the place of the external functions called by the Copilot +-- monitor when a guarded condition occurs. +-- +-- Each trigger statement has a corresponding global variable that +-- is used to record if the trigger function was called; initially +-- the global is false, and is set to true when the trigger function +-- is called. At the end of verification, we check that the value +-- of this global variable is true iff the corresponding trigger guard +-- condition is true. +-- +-- The other function of the trigger overrides is to check that, when called, +-- the functions are given the expected argument values. +-- +-- Otherwise, the override functions have no effects, which corresponds +-- to the assumption that the external environment makes no changes to the +-- program state that are observable to the Copilot monitor. +triggerOverride :: forall sym t arch msgs. + IsSymInterface sym => + Log.Logs msgs => + Log.SupportsCopilotLogMessage msgs => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + (?intrinsicsOpts :: IntrinsicsOptions) => + (1 <= ArchWidth arch) => + HasPtrWidth (ArchWidth arch) => + HasLLVMAnn sym => + + CopilotLogRefs sym -> + (Name, GlobalVar NatType, Pred sym) -> + (Name, BoolExpr t, [(Some Type, CW4.XExpr sym)]) -> + OverrideTemplate (Crux sym) sym LLVM arch +triggerOverride clRefs (_,triggerGlobal,_) (nm, _guard, args) = + let args' = map toTypeRepr args in + case Ctx.fromList args' of + Some argCtx -> + basic_llvm_override $ + LLVMOverride decl argCtx UnitRepr $ + \memOps calledArgs -> + ovrWithBackend $ \bak -> + do let sym = backendGetSym bak + modifyGlobal triggerGlobal $ \count -> do + -- See Note [Global variables for trigger functions] + countPlusOne <- liftIO $ do + one <- natLit sym 1 + natAdd sym count one + pure ((), countPlusOne) + mem <- readGlobal memOps + liftIO $ checkArgs bak mem (toListFC Some calledArgs) args + return () + + where + decl = L.Declare + { L.decLinkage = Nothing + , L.decVisibility = Nothing + , L.decRetType = L.PrimType L.Void + , L.decName = L.Symbol nm + , L.decArgs = map llvmArgTy args + , L.decVarArgs = False + , L.decAttrs = [] + , L.decComdat = Nothing + } + + -- Use the `-CompositePtr` functions here to ensure that arguments with array + -- or struct types are treated as pointers. See Note [Arrays and structs]. + toTypeRepr (Some ctp, _) = llvmTypeAsRepr (copilotTypeToMemTypeCompositePtr (llvmDataLayout ?lc) ctp) Some + llvmArgTy (Some ctp, _) = copilotTypeToLLVMTypeCompositePtr ctp + + checkArgs :: forall bak. IsSymBackend sym bak => + bak -> MemImpl sym -> [Some (RegEntry sym)] -> [(Some Type, CW4.XExpr sym)] -> IO () + checkArgs bak mem = loop (0::Integer) + where + loop i (x:xs) ((ctp,v):vs) = checkArg bak mem i x ctp v >> loop (i+1) xs vs + loop _ [] [] = return () + loop _ _ _ = fail $ "Argument list mismatch in " ++ nm + + checkArg :: forall bak. IsSymBackend sym bak => + bak -> MemImpl sym -> Integer -> Some (RegEntry sym) -> Some Type -> CW4.XExpr sym -> IO () + checkArg bak mem i (Some (RegEntry tp v)) (Some ctp) x = + do let sym = backendGetSym bak + eq <- computeEqualVals bak clRefs mem ctp x tp v + (ann, eq') <- annotateTerm sym eq + let shortmsg = "Trigger " ++ show nm ++ " argument " ++ show i + let longmsg = show (printSymExpr eq') + let rsn = AssertFailureSimError shortmsg longmsg + loc <- getCurrentProgramLoc sym + modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert (BoolAnn ann) + $ Log.TriggerArgumentEqualityAssertion + $ Log.SomeSome + $ Log.TriggerArgumentEquality sym loc nm i ctp x tp v + addDurableProofObligation bak (LabeledPred eq' (SimError loc rsn)) + + +-- | Actually execute the Crucible simulator on the generated "step" function. +-- This will record proof side-conditions into the symbolic backend, and +-- return the memory state corresponding to the function post-state. +-- +-- This function will record side-conditions that arise from the semantics +-- of C itself (e.g., memory is accessed in bounds and signed arithmetic +-- doesn't overflow) as well as the conditions related to trigger functions. +executeStep :: forall sym arch msgs. + IsSymInterface sym => + Log.Logs msgs => + Log.SupportsCruxLLVMLogMessage msgs => + Log.SupportsCopilotLogMessage msgs => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + (?intrinsicsOpts :: IntrinsicsOptions) => + (1 <= ArchWidth arch) => + HasPtrWidth (ArchWidth arch) => + HasLLVMAnn sym => + + VerifierOptions -> + CSettings -> + CopilotLogRefs sym -> + SimCtxt Crux sym LLVM -> + GlobalVar Mem -> + MemImpl sym -> + L.Module -> + ModuleTranslation arch -> + [(Name, GlobalVar NatType, Pred sym)] -> + [OverrideTemplate (Crux sym) sym LLVM arch] -> + [Pred sym] {- User-provided property assumptions -} -> + [Pred sym] {- Side conditions related to partial operations -} -> + IO (MemImpl sym) +executeStep opts csettings clRefs simctx memVar mem llvmmod modTrans triggerGlobals triggerOverrides assums sideConds = + do globSt <- foldM setupTrigger (llvmGlobals memVar mem) triggerGlobals + let initSt = InitialState simctx globSt defaultAbortHandler memRepr $ + runOverrideSim memRepr runStep + res <- executeCrucible [] initSt + case res of + FinishedResult _ pr -> return (pr^.partialValue.gpValue.to regValue) + AbortedResult _ abortRes -> fail $ show $ ppAbortedResult abortRes + TimeoutResult{} -> fail "simulation timed out!" + + where + -- See Note [Global variables for trigger functions] + setupTrigger gs (_,gv,_) = do + zero <- liftIO $ natLit sym 0 + pure $ insertGlobal gv zero gs + llvm_ctx = modTrans ^. transContext + stepName = cSettingsStepFunctionName csettings + sym = simctx^.ctxSymInterface + + -- TODO, would be lovely to be able to do better than dummy positions for all these things + -- so we can correspond assumptions and asserts back to the parts of the original spec that + -- gave rise to them. + dummyLoc = mkProgramLoc "<>" InternalPos + + assumeProperty b = + withBackend simctx $ \bak -> + addAssumption bak (GenericAssumption dummyLoc "Property assumption" b) + + assumeSideCond b = + withBackend simctx $ \bak -> + addAssumption bak (GenericAssumption dummyLoc "Side condition for partial operation" b) + + ppAbortedResult :: AbortedResult sym ext -> PP.Doc ann + ppAbortedResult abortRes = + case gatherReasons abortRes of + reason :| [] -> reason + reasons -> PP.vcat $ "Simulation aborted for multiple reasons." + : NE.toList reasons + + gatherReasons :: AbortedResult sym ext -> NonEmpty (PP.Doc ann) + gatherReasons (AbortedExec rsn _) = + PP.vcat ["Simulation aborted!", ppAbortExecReason rsn] :| [] + gatherReasons (AbortedExit ec) = + PP.vcat ["Simulation called exit!", PP.viaShow ec] :| [] + gatherReasons (AbortedBranch _ _ t f) = + gatherReasons t <> gatherReasons f + + -- Simulator entry point + runStep :: OverrideSim (Crux sym) sym LLVM (RegEntry sym Mem) EmptyCtx Mem (MemImpl sym) + runStep = + do -- set up built-in functions and trigger overrides + _ <- register_llvm_overrides llvmmod [] triggerOverrides llvm_ctx + -- set up functions defined in the module + registerLazyModule sayTranslationWarning modTrans + + -- make any property assumptions + liftIO (mapM_ assumeProperty assums) + + -- assume side conditions related to partial operations + when (assumePartialSideConds opts) $ liftIO $ + mapM_ assumeSideCond sideConds + + -- look up and call the step function + mbCfg <- liftIO $ getTranslatedCFG modTrans (L.Symbol stepName) + () <- case mbCfg of + Just (_, AnyCFG anyCfg, warns) -> do + liftIO $ mapM_ sayTranslationWarning warns + case (cfgArgTypes anyCfg, cfgReturnType anyCfg) of + (Empty, UnitRepr) -> regValue <$> callCFG anyCfg emptyRegMap + _ -> fail $ unwords [show stepName, "should take no arguments and return void"] + Nothing -> fail $ unwords ["Could not find step function named", show stepName] + + -- Assert that the trigger functions were called exactly once iff the + -- associated guard condition was true. + -- See Note [Global variables for trigger functions]. + forM_ triggerGlobals $ \(nm, gv, guard) -> + do expectedCount <- liftIO $ do + one <- natLit sym 1 + zero <- natLit sym 0 + natIte sym guard one zero + actualCount <- readGlobal gv + eq <- liftIO $ natEq sym expectedCount actualCount + (ann, eq') <- liftIO $ annotateTerm sym eq + let shortmsg = "Trigger guard equality condition: " ++ show nm + let longmsg = show (printSymExpr eq') + let rsn = AssertFailureSimError shortmsg longmsg + liftIO + $ modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert (BoolAnn ann) + $ Log.TriggersInvokedCorrespondinglyAssertion + $ Log.TriggersInvokedCorrespondingly dummyLoc nm expectedCount actualCount + withBackend simctx $ \bak -> + liftIO $ addDurableProofObligation bak (LabeledPred eq' (SimError dummyLoc rsn)) + + -- return the final state of the memory + readGlobal memVar + +-- | Given a bisimulation proof bundle and an empty initial state, +-- populate the global ring-buffer variables with abstract state +-- values, and write the abstract values of the external stream +-- values into their proper locations. +setupPrestate :: + IsSymBackend sym bak => + HasPtrWidth wptr => + HasLLVMAnn sym => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + + bak -> + MemImpl sym -> + CW4.BisimulationProofBundle sym -> + IO (MemImpl sym) +setupPrestate bak mem0 prfbundle = + do mem' <- foldM setupStreamState mem0 (CW4.streamState (CW4.preStreamState prfbundle)) + foldM setupExternalInput mem' (CW4.externalInputs prfbundle) + + where + sym = backendGetSym bak + + sizeTStorage :: StorageType + sizeTStorage = bitvectorType (bitsToBytes (intValue ?ptrWidth)) + + setupExternalInput mem (nm, Some ctp, v) = + do -- Compute LLVM/Crucible type information from the Copilot type + let memTy = copilotTypeToMemTypeBool8 (llvmDataLayout ?lc) ctp + let typeAlign = memTypeAlign (llvmDataLayout ?lc) memTy + stType <- toStorableType memTy + Some typeRepr <- return (llvmTypeAsRepr memTy Some) + + -- resolve the global variable to a pointers + ptrVal <- doResolveGlobal bak mem (L.Symbol nm) + + -- write the value into the global + regVal <- copilotExprToRegValue sym v typeRepr + doStore bak mem ptrVal typeRepr stType typeAlign regVal + + setupStreamState mem (nm, Some ctp, vs) = + do -- TODO, should get these from somewhere inside copilot instead of building these names directly + let idxName = "s" ++ show nm ++ "_idx" + let bufName = "s" ++ show nm + let buflen = genericLength vs :: Integer + + -- Compute LLVM/Crucible type information from the Copilot type + let memTy = copilotTypeToMemTypeBool8 (llvmDataLayout ?lc) ctp + let typeLen = memTypeSize (llvmDataLayout ?lc) memTy + let typeAlign = memTypeAlign (llvmDataLayout ?lc) memTy + stType <- toStorableType memTy + Some typeRepr <- return (llvmTypeAsRepr memTy Some) + + -- Resolve the global names into base pointers + idxPtr <- doResolveGlobal bak mem (L.Symbol idxName) + bufPtr <- doResolveGlobal bak mem (L.Symbol bufName) + + -- Create a fresh index value in the proper range + idxVal <- freshBoundedBV sym (safeSymbol idxName) ?ptrWidth + (Just 0) (Just (fromIntegral (buflen - 1))) + idxVal' <- llvmPointer_bv sym idxVal + + -- store the index value in the correct location + let sizeTAlign = memTypeAlign (llvmDataLayout ?lc) (IntType (natValue ?ptrWidth)) + mem' <- doStore bak mem idxPtr (LLVMPointerRepr ?ptrWidth) sizeTStorage sizeTAlign idxVal' + + buflen' <- bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth buflen) + typeLen' <- bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth (toInteger typeLen)) + + -- Write each value of the stream ring buffer into its correct location + flip execStateT mem' $ + forM_ (zip vs [0 ..]) $ \(v,i) -> + do ptrVal <- lift $ + do x1 <- bvAdd sym idxVal =<< bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth i) + x2 <- bvUrem sym x1 buflen' + x3 <- bvMul sym x2 typeLen' + ptrAdd sym ?ptrWidth bufPtr x3 + + regVal <- lift $ copilotExprToRegValue sym v typeRepr + StateT $ \m -> + do m' <- doStore bak m ptrVal typeRepr stType typeAlign regVal + return ((),m') + +-- | Given a memory image and a "proof state", assert that the global values +-- for each stream ring buffer have values that correspond to the given +-- stream state. This collection of assertions defines the bisimulation +-- relation. +assertStateRelation :: + IsSymBackend sym bak => + Log.Logs msgs => + HasPtrWidth wptr => + HasLLVMAnn sym => + (?memOpts :: MemOptions) => + (?lc :: TypeContext) => + bak -> + CopilotLogRefs sym -> + MemImpl sym -> + Log.StateRelationStep -> + CW4.BisimulationProofState sym -> + IO () +assertStateRelation bak clRefs mem stateRelStep prfst = + -- For each stream in the proof state, assert that the + -- generated ring buffer global contains the corresponding + -- values. + forM_ (CW4.streamState prfst) assertStreamState + + where + sym = backendGetSym bak + + sizeTStorage :: StorageType + sizeTStorage = bitvectorType (bitsToBytes (intValue ?ptrWidth)) + + assertStreamState (nm, Some ctp, vs) = + do -- TODO, should get these from somewhere inside copilot instead of building these names directly + let idxName = "s" ++ show nm ++ "_idx" + let bufName = "s" ++ show nm + let buflen = genericLength vs :: Integer + + -- Compute LLVM/Crucible type information from the Copilot type + let memTy = copilotTypeToMemTypeBool8 (llvmDataLayout ?lc) ctp + let typeLen = memTypeSize (llvmDataLayout ?lc) memTy + let typeAlign = memTypeAlign (llvmDataLayout ?lc) memTy + stType <- toStorableType memTy + Some typeRepr <- return (llvmTypeAsRepr memTy Some) + + -- Resolve the global names into base pointers + idxPtr <- doResolveGlobal bak mem (L.Symbol idxName) + bufPtr <- doResolveGlobal bak mem (L.Symbol bufName) + + -- read the value of the ring buffer index + let sizeTAlign = memTypeAlign (llvmDataLayout ?lc) (IntType (natValue ?ptrWidth)) + (bannIdxVal, pIdxVal, idxVal) <- + doLoadWithAnn bak mem idxPtr sizeTStorage (LLVMPointerRepr ?ptrWidth) sizeTAlign + idxVal' <- projectLLVM_bv bak idxVal + locIdxVal <- getCurrentProgramLoc sym + modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert bannIdxVal + $ Log.RingBufferIndexLoadAssertion + $ Log.RingBufferIndexLoad sym stateRelStep locIdxVal (Text.pack idxName) pIdxVal + + buflen' <- bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth buflen) + typeLen' <- bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth (toInteger typeLen)) + + -- For each value in the stream description, read a corresponding value from + -- memory and assert that they are equal. + forM_ (zip vs [0 ..]) $ \(v,i) -> + do ptrVal <- + do x1 <- bvAdd sym idxVal' =<< bvLit sym ?ptrWidth (BV.mkBV ?ptrWidth i) + x2 <- bvUrem sym x1 buflen' + x3 <- bvMul sym x2 typeLen' + ptrAdd sym ?ptrWidth bufPtr x3 + + (bannv', pv', v') <- doLoadWithAnn bak mem ptrVal stType typeRepr typeAlign + locv' <- getCurrentProgramLoc sym + let bufNameT = Text.pack bufName + modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert bannv' + $ Log.RingBufferLoadAssertion + $ Log.SomeSome + $ Log.RingBufferLoad sym stateRelStep locv' bufNameT i buflen ctp typeRepr pv' + eq <- computeEqualVals bak clRefs mem ctp v typeRepr v' + (ann, eq') <- annotateTerm sym eq + let shortmsg = "State equality condition: " ++ show nm ++ " index value " ++ show i + let longmsg = show (printSymExpr eq') + let rsn = AssertFailureSimError shortmsg longmsg + let loc = mkProgramLoc "<>" InternalPos + modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert (BoolAnn ann) + $ Log.StreamValueEqualityAssertion + $ Log.SomeSome + $ Log.StreamValueEquality sym stateRelStep loc bufNameT i buflen ctp v typeRepr v' + addDurableProofObligation bak (LabeledPred eq' (SimError loc rsn)) + + return () + +-- | Translate the @XExpr@ values from the "Copilot.Theorem.What4" module into +-- Crucible @RegValue@s suitable for injection into the Crucible simulator. +copilotExprToRegValue :: forall sym tp. + IsSymInterface sym => + sym -> + CW4.XExpr sym -> + TypeRepr tp -> + IO (RegValue sym tp) +copilotExprToRegValue sym = loop + where + loop :: forall tp'. CW4.XExpr sym -> TypeRepr tp' -> IO (RegValue sym tp') + + loop (CW4.XBool b) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @1) = + llvmPointer_bv sym =<< predToBV sym b knownRepr + loop (CW4.XBool b) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @8) = + llvmPointer_bv sym =<< predToBV sym b knownRepr + loop (CW4.XInt8 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @8) = + llvmPointer_bv sym x + loop (CW4.XInt16 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @16) = + llvmPointer_bv sym x + loop (CW4.XInt32 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @32) = + llvmPointer_bv sym x + loop (CW4.XInt64 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @64) = + llvmPointer_bv sym x + loop (CW4.XWord8 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @8) = + llvmPointer_bv sym x + loop (CW4.XWord16 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @16) = + llvmPointer_bv sym x + loop (CW4.XWord32 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @32) = + llvmPointer_bv sym x + loop (CW4.XWord64 x) (LLVMPointerRepr w) | Just Refl <- testEquality w (knownNat @64) = + llvmPointer_bv sym x + + loop (CW4.XFloat x) (FloatRepr SingleFloatRepr) = return x + loop (CW4.XDouble x) (FloatRepr DoubleFloatRepr) = return x + + loop (CW4.XEmptyArray _ctp) (VectorRepr _tpr) = + pure V.empty + loop (CW4.XArray xs) (VectorRepr tpr) = + V.generateM (PVec.lengthInt xs) (\i -> loop (PVec.elemAtUnsafe i xs) tpr) + loop (CW4.XStruct xs) (StructRepr ctx) = + Ctx.traverseWithIndex + (\i tpr -> RV <$> loop (xs !! Ctx.indexVal i) tpr) + ctx + + loop x tpr = + fail $ unlines ["Mismatch between Copilot value and crucible value", show x, show tpr] + + +-- | Given an @XExpr@ from from the "Copilot.Theorem.What4" module, and +-- a Crucible @RegValue@ which is expected to match, compute an equality +-- predicate between the values. The Crucible values may be pointers, +-- requiring us to resolve the indirection through memory; this is necessary +-- for array and struct values, but would also work for scalars. +computeEqualVals :: forall sym bak tp a wptr. + IsSymBackend sym bak => + HasPtrWidth wptr => + HasLLVMAnn sym => + (?lc :: TypeContext) => + (?memOpts :: MemOptions) => + bak -> + CopilotLogRefs sym -> + MemImpl sym -> + Type a -> + CW4.XExpr sym -> + TypeRepr tp -> + RegValue sym tp -> + IO (Pred sym) +computeEqualVals bak clRefs mem = loop + where + sym = backendGetSym bak + + loop :: forall tp' a'. Type a' -> CW4.XExpr sym -> TypeRepr tp' -> RegValue sym tp' -> IO (Pred sym) + loop Bool (CW4.XBool b) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @1) = + isEq sym b =<< bvIsNonzero sym =<< projectLLVM_bv bak v + loop Bool (CW4.XBool b) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @8) = + isEq sym b =<< bvIsNonzero sym =<< projectLLVM_bv bak v + loop Int8 (CW4.XInt8 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @8) = + bvEq sym x =<< projectLLVM_bv bak v + loop Int16 (CW4.XInt16 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @16) = + bvEq sym x =<< projectLLVM_bv bak v + loop Int32 (CW4.XInt32 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @32) = + bvEq sym x =<< projectLLVM_bv bak v + loop Int64 (CW4.XInt64 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @64) = + bvEq sym x =<< projectLLVM_bv bak v + loop Word8 (CW4.XWord8 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @8) = + bvEq sym x =<< projectLLVM_bv bak v + loop Word16 (CW4.XWord16 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @16) = + bvEq sym x =<< projectLLVM_bv bak v + loop Word32 (CW4.XWord32 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @32) = + bvEq sym x =<< projectLLVM_bv bak v + loop Word64 (CW4.XWord64 x) (LLVMPointerRepr w) v | Just Refl <- testEquality w (knownNat @64) = + bvEq sym x =<< projectLLVM_bv bak v + + loop Float (CW4.XFloat x) (FloatRepr SingleFloatRepr) v = iFloatEq @_ @SingleFloat sym x v + loop Double (CW4.XDouble x) (FloatRepr DoubleFloatRepr) v = iFloatEq @_ @DoubleFloat sym x v + + loop (Array _ctp) (CW4.XEmptyArray _ctp2) (VectorRepr _tpr) vs = + pure $ backendPred sym $ V.null vs + loop (Array ctp) (CW4.XArray xs) (VectorRepr tpr) vs + | PVec.lengthInt xs == V.length vs + = V.ifoldM (\pAcc i v -> andPred sym pAcc =<< loop ctp (PVec.elemAtUnsafe i xs) tpr v) + (truePred sym) vs + | otherwise + = pure (falsePred sym) + loop (Struct struct) (CW4.XStruct xs) (StructRepr ctx) vs + | length copilotVals == Ctx.sizeInt (Ctx.size vs) + = ifoldlMFC (\i pAcc tpr -> + case copilotVals !! Ctx.indexVal i of + (Value ctp _, x) -> + andPred sym pAcc =<< loop ctp x tpr (unRV (vs Ctx.! i))) + (truePred sym) ctx + | otherwise + = pure (falsePred sym) + where + copilotVals :: [(Value a', CW4.XExpr sym)] + copilotVals = zip (toValues struct) xs + + -- If we encounter a pointer, read the memory that it points to and recurse, + -- using the Copilot type as a guide for how much memory to read. This is + -- needed to make array- or struct-typed arguments work (see + -- Note [Arrays and structs]), although there is nothing about this code + -- that is array- or struct-specific. In fact, this code could also work + -- for pointer arguments of any other type. + loop ctp x PtrRepr v = + do let memTy = copilotTypeToMemTypeBool8 (llvmDataLayout ?lc) ctp + typeAlign = memTypeAlign (llvmDataLayout ?lc) memTy + stp <- toStorableType memTy + llvmTypeAsRepr memTy $ \tpr -> + do loc <- getCurrentProgramLoc sym + (bann, p, regVal) <- doLoadWithAnn bak mem v stp tpr typeAlign + modifyIORef' (verifierAssertionMapRef clRefs) + $ Map.insert bann + $ Log.PointerArgumentLoadAssertion + $ Log.SomeSome + $ Log.PointerArgumentLoad sym loc ctp tpr p + loop ctp x tpr regVal + + loop _ctp x tpr _v = + fail $ unlines ["Mismatch between Copilot value and crucible value", show x, show tpr] + +-- | Convert a Copilot 'CT.Type' to a Crucible 'MemType'. 'CT.Bool's are +-- assumed to be one bit in size. See @Note [How LLVM represents bool]@. +copilotTypeToMemType :: + DataLayout -> + CT.Type a -> + MemType +copilotTypeToMemType dl = loop + where + loop :: forall t. CT.Type t -> MemType + loop CT.Bool = i1 + loop CT.Int8 = i8 + loop CT.Int16 = i16 + loop CT.Int32 = i32 + loop CT.Int64 = i64 + loop CT.Word8 = i8 + loop CT.Word16 = i16 + loop CT.Word32 = i32 + loop CT.Word64 = i64 + loop CT.Float = FloatType + loop CT.Double = DoubleType + loop t0@(CT.Array tp) = + let len = fromIntegral (typeLength t0) in + ArrayType len (copilotTypeToMemTypeBool8 dl tp) + loop (CT.Struct v) = + StructType (mkStructInfo dl False (map val (CT.toValues v))) + + val :: forall t. CT.Value t -> MemType + val (CT.Value tp _) = copilotTypeToMemTypeBool8 dl tp + +-- | Like 'copilotTypeToMemType', except that 'CT.Bool's are assumed to be +-- eight bits, not one bit. See @Note [How LLVM represents bool]@. +copilotTypeToMemTypeBool8 :: + DataLayout -> + CT.Type a -> + MemType +copilotTypeToMemTypeBool8 _dl CT.Bool = i8 +copilotTypeToMemTypeBool8 dl tp = copilotTypeToMemType dl tp + +-- | Like 'copilotTypeToMemType', except that composite types (i.e., +-- 'CT.Array's and 'CT.Struct's) are converted to 'PtrType's instead of direct +-- 'ArrayType's or 'StructType's. See @Note [Arrays and structs]@. +copilotTypeToMemTypeCompositePtr :: + DataLayout -> + CT.Type a -> + MemType +copilotTypeToMemTypeCompositePtr dl (CT.Array tp) = + PtrType (MemType (copilotTypeToMemTypeBool8 dl tp)) +copilotTypeToMemTypeCompositePtr _dl (CT.Struct struct) = + PtrType (Alias (copilotStructIdent struct)) +copilotTypeToMemTypeCompositePtr dl tp = copilotTypeToMemType dl tp + +-- | Convert a Copilot 'CT.Type' to an LLVM 'L.Type'. 'CT.Bool's are +-- assumed to be one bit in size. See @Note [How LLVM represents bool]@. +copilotTypeToLLVMType :: + CT.Type a -> + L.Type +copilotTypeToLLVMType = loop + where + loop :: forall t. CT.Type t -> L.Type + loop CT.Bool = L.PrimType (L.Integer 1) + loop CT.Int8 = L.PrimType (L.Integer 8) + loop CT.Int16 = L.PrimType (L.Integer 16) + loop CT.Int32 = L.PrimType (L.Integer 32) + loop CT.Int64 = L.PrimType (L.Integer 64) + loop CT.Word8 = L.PrimType (L.Integer 8) + loop CT.Word16 = L.PrimType (L.Integer 16) + loop CT.Word32 = L.PrimType (L.Integer 32) + loop CT.Word64 = L.PrimType (L.Integer 64) + loop CT.Float = L.PrimType (L.FloatType L.Float) + loop CT.Double = L.PrimType (L.FloatType L.Double) + loop t0@(CT.Array tp) = + let len = fromIntegral (typeLength t0) in + L.Array len (copilotTypeToLLVMTypeBool8 tp) + loop (CT.Struct v) = + -- NB: Don't use L.Struct here. That represents a literal, unnamed + -- struct, but all of the structs used in a copilot-c99 program are + -- named structs. As such, we must identify the struct by its alias. + L.Alias (copilotStructIdent v) + +-- | Like 'copilotTypeToLLVMType', except that 'CT.Bool's are assumed to be +-- eight bits, not one bit. See @Note [How LLVM represents bool]@. +copilotTypeToLLVMTypeBool8 :: + CT.Type a -> + L.Type +copilotTypeToLLVMTypeBool8 CT.Bool = L.PrimType (L.Integer 8) +copilotTypeToLLVMTypeBool8 tp = copilotTypeToLLVMType tp + +-- | Like 'copilotTypeToLLVMType', except that composite types (i.e., +-- 'CT.Array's and 'CT.Struct's) are given special treatment involving +-- pointers. See @Note [Arrays and structs]@. +copilotTypeToLLVMTypeCompositePtr :: + CT.Type a -> + L.Type +copilotTypeToLLVMTypeCompositePtr (CT.Array tp) = + L.PtrTo (copilotTypeToLLVMTypeBool8 tp) +copilotTypeToLLVMTypeCompositePtr (CT.Struct struct) = + L.PtrTo (L.Alias (copilotStructIdent struct)) +copilotTypeToLLVMTypeCompositePtr tp = copilotTypeToLLVMType tp + +-- | Given a struct @s@, construct the name @struct.s@ as an LLVM identifier. +copilotStructIdent :: Struct a => a -> L.Ident +copilotStructIdent struct = L.Ident $ "struct." ++ typeName struct + +{- +Note [How LLVM represents bool] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +How are C values of type `bool` represented in LLVM? It depends. If it's being +stored directly a `bool`, it's represented with `i1` (i.e., a single bit). If +a `bool` is a member of some composite type, such as a pointer, array, or +struct, however, it's representing with `i8` (i.e., eight bits). This means +that we have to be careful when converting Bool-typed Copilot values, as they +can become `i1` or `i8` depending on the context. + +copilot-verifier handles this by having both `copilotTypeToLLVMType` and +`copilotTypeToLLVMTypeBool8` functions. The former function treats `bool`s as +`i1`, whereas the latter treats `bool`s as `i8`. The former is used when +converting "top-level" types (e.g., the argument types in a trigger override), +whereas the latter is used when converting types that are part of a larger +composite type (e.g., the element type in an array). + +The story for the `copilotTypeToMemType` and `copilotTypeToMemTypeBool8` +functions is similar. + +Note [Arrays and structs] +~~~~~~~~~~~~~~~~~~~~~~~~~ +When Clang compiles a function with an array argument, such as this trigger +function: + + void func(int32_t func_arg0[2]) { ... } + +It will produce the following LLVM code: + + declare void @func(i32*) { ... } + +Note that the argument is an i32*, not a [2 x i32]. As a result, we can't +translate Copilot array types directly to LLVM array types when they're used as +arguments to a function. This impedance mismatch is handled in two places: + +1. The `copilotTypeToMemTypeCompositePtr`/`copilotTypeToLLVMTypeCompositePtr` + functions special-case Copilot arrays such that they are translated to + pointers. These functions are used when declaring the argument types of an + override for a trigger function (see `triggerOverride`). +2. The `computeEqualVals` function has a special case for pointer + arguments—see the case that matches on `PtrRepr`. When a `PtrRepr` is + encountered, the underlying array values that it points to are read from + memory. Because `PtrRepr` doesn't record the type of the thing being pointed + to, `computeEqualVals` uses the corresponding Copilot type as a guide to + determine how much memory to read and at what type the memory should be + used. After this, `computeEqualVals` reads from the read array + element-by-element—see the `VectorRepr` cases. + + Note that unlike `computeEqualVals`, `copilotExprToRegValue` does not need + a `PtrRepr` case. This is because `copilotExprToRegValue` is ultimately used + in service of calling writing elements of streams to memory, and streams do + not store pointer values (at least, not in today's Copilot). + +There is a very similar story for structs. Copilot passes structs by reference +in trigger functions (e.g., `void trigger(struct s *ss)`), so we must also load +from a `PtrRepr` in `computeEqualVals` to handle structs. + +See Note [Global variables for trigger functions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +As part of verifying that the behavior of a Copilot specification's trigger +functions behave the same way as the trigger functions in the corresponding C +program, we check that each trigger function in the C program is invoked the +appropriate number of times. That is, if the guard condition for a trigger is +true, the C trigger function should be invoked exactly once, and if the guard +condition is false, then the trigger function should not be invoked at all. + +To check this, we create a Nat-valued global variable for each trigger function +and initialize it to zero. Whenever we simulate a trigger function, we increment +the value of the corresponding global variable. At the end of simulation, we +check that the value in each global variable is equal to +`if guard_cond then 1 else 0`. +-} + +-- | Like @crucible-llvm@'s @doLoad@, but with the following differences: +-- +-- * This function returns the 'BoolAnn' and 'Pred' asserting the validity of +-- the load, which the verifier needs for logging purposes. +-- +-- * This always generates a durable proof goal so that Crucible will always +-- record it, even if it is trivial. +doLoadWithAnn :: + ( IsSymBackend sym bak, HasPtrWidth wptr, HasLLVMAnn sym + , ?memOpts :: MemOptions ) => + bak -> + MemImpl sym -> + LLVMPtr sym wptr {- ^ pointer to load from -} -> + StorageType {- ^ type of value to load -} -> + TypeRepr tp {- ^ crucible type of the result -} -> + Alignment {- ^ assumed pointer alignment -} -> + IO (BoolAnn sym, Pred sym, RegValue sym tp) +doLoadWithAnn bak mem ptr valType tpr alignment = do + partLLVMVal <- loadRaw sym mem ptr valType alignment + (bann, p, llvmVal) <- assertSafeWithAnn bak partLLVMVal + regVal <- unpackMemValue sym tpr llvmVal + pure (bann, p, regVal) + where + sym = backendGetSym bak + +-- | Like @crucible-llvm@'s @assertSafe@, but with the following differences: +-- +-- * This function returns the 'BoolAnn' and 'Pred' corresponding to the +-- assertion, which the verifier needs for logging purposes. +-- +-- * This generates a durable assertion so that Crucible will always record it, +-- even if it is trivial. +assertSafeWithAnn :: + IsSymBackend sym bak => + bak -> + PartLLVMVal sym -> + IO (BoolAnn sym, Pred sym, LLVMVal sym) +assertSafeWithAnn bak partVal = do + loc <- getCurrentProgramLoc sym + let err = SimError loc rsn + case partVal of + NoErr p v -> do + (ann, p') <- annotateTerm sym p + addDurableAssertion bak (LabeledPred p' err) + return (BoolAnn ann, p', v) + Err p -> do + (_ann, p') <- annotateTerm sym p + addDurableProofObligation bak (LabeledPred p' err) + abortExecBecause (AssertionFailure err) + where + rsn = AssertFailureSimError "Error during memory load" "" + sym = backendGetSym bak + +-- | Given a simulator state, extract any collected proof obligations, +-- attempt to prove them, and present the results to the user. +-- +-- Afterward, the simulator state will be cleared of any proof obligations, +-- regardless of if they could all be proved. +proveObls :: + IsSymInterface sym => + sym ~ ExprBuilder t st fs => + Log.Logs msgs => + Log.SupportsCruxLogMessage msgs => + Log.SupportsCopilotLogMessage msgs => + CruxOptions -> + [SolverAdapter st] -> + CopilotLogRefs sym -> + Log.VerificationStep -> + SimCtxt Crux sym LLVM -> + IO Integer +proveObls cruxOpts adapters clRefs step simctx = + withBackend simctx $ \bak -> + do let sym = backendGetSym bak + obls <- getProofObligations bak + clearProofObligations bak + +-- mapM_ (print . ppSimError) (summarizeObls sym obls) + + vaMap <- readIORef $ verifierAssertionMapRef clRefs + let laMapRef = llvmAnnMapRef clRefs + laMap <- readIORef laMapRef + results <- proveGoalsOffline adapters cruxOpts simctx (explainFailure sym laMapRef) obls + presentResults sym vaMap laMap step results + +{- +summarizeObls :: sym -> ProofObligations sym -> [SimError] +summarizeObls _ Nothing = [] +summarizeObls _ (Just obls) = map (view labeledPredMsg . proofGoal) (goalsToList obls) +-} + +-- | Compute the number of goals that were proven during verification, logging +-- the goals as they are proven. If all goals are proven successfully, return +-- the number of goals. Otherwise, log the failing proof goals and abort. +presentResults :: + Log.Logs msgs => + Log.SupportsCopilotLogMessage msgs => + IsSymInterface sym => + sym -> + Map.Map (BoolAnn sym) (Log.VerifierAssertion sym) -> + LLVMAnnMap sym -> + Log.VerificationStep -> + (ProcessedGoals, Maybe (Goals (Assumptions sym) (Assertion sym, [ProgramLoc], ProofResult sym))) -> + IO Integer +presentResults sym vaMap laMap step (num, goals) + | numTotalGoals == 0 + = do Log.sayCopilot Log.AllGoalsProved + pure 0 + + -- All goals were proven + | numProvedGoals == numTotalGoals + = do traverse_ (logVerifierAssertions sym vaMap laMap step num) goals + printGoals + pure numProvedGoals + + -- There were some unproved goals, so fail with exit code 1 + | otherwise + = do printGoals + exitFailure + where + numTotalGoals = totalProcessedGoals num + numProvedGoals = provedGoals num + + printGoals = + do Log.sayCopilot $ Log.OnlySomeGoalsProved numProvedGoals numTotalGoals + goals' <- provedGoalsTree sym goals + case goals' of + Just g -> Log.logGoal g + Nothing -> return () + +-- | Upon a successful verification, log the various assertions that the +-- verifier makes. These assertions will be visible in the output if the +-- 'verbosity' is set to 'Noisy'. +logVerifierAssertions :: + forall sym msgs. + IsSymInterface sym => + Log.Logs msgs => + Log.SupportsCopilotLogMessage msgs => + sym -> + Map.Map (BoolAnn sym) (Log.VerifierAssertion sym) -> + LLVMAnnMap sym -> + Log.VerificationStep -> + ProcessedGoals -> + Goals (Assumptions sym) (Assertion sym, [ProgramLoc], ProofResult sym) -> + IO () +logVerifierAssertions sym vaMap laMap step num goals = void $ go 0 goals + where + numTotalGoals = totalProcessedGoals num + + go :: Integer -> + Goals (Assumptions sym) (Assertion sym, [ProgramLoc], ProofResult sym) -> + IO Integer + go goalIdx gs = + case gs of + Assuming _ gs' -> + go goalIdx gs' + + Prove (gl, locs, _) -> do + let p = gl^.labeledPred + nearestLoc = nearestProgramLoc locs + + -- First, obtain the verifier assertion. + va <- case getAnnotation sym p of + -- If the assertion has a BoolAnn, look it up in the assertion maps + -- that we have accumulated during verification. + Just ann + | Just va <- Map.lookup (BoolAnn ann) vaMap + -> pure va + | Just (stk, bb) <- Map.lookup (BoolAnn ann) laMap + -> pure $ Log.LLVMBadBehaviorCheckAssertion + $ Log.LLVMBadBehaviorCheck sym nearestLoc stk bb p + | otherwise + -> fail $ unlines + [ "Cannot find BoolAnn for assertion" + , show $ gl^.labeledPredMsg + , show $ printSymExpr p + ] + -- If the assertion does not have a BoolAnn, fall back to using + -- heuristics to guess what kind of assertion it is. + Nothing -> pure $ verifierAssertionHeuristics sym nearestLoc p + + -- Log the assertion. + case va of + Log.StreamValueEqualityAssertion (Log.SomeSome a) -> + Log.sayCopilot $ + Log.StreamValueEqualityProofGoal step goalIdx numTotalGoals a + Log.TriggersInvokedCorrespondinglyAssertion a -> + Log.sayCopilot $ + Log.TriggersInvokedCorrespondinglyProofGoal step goalIdx numTotalGoals a + Log.TriggerArgumentEqualityAssertion (Log.SomeSome a) -> + Log.sayCopilot $ + Log.TriggerArgumentEqualityProofGoal step goalIdx numTotalGoals a + Log.RingBufferLoadAssertion (Log.SomeSome a) -> + Log.sayCopilot $ + Log.RingBufferLoadProofGoal step goalIdx numTotalGoals a + Log.RingBufferIndexLoadAssertion a -> + Log.sayCopilot $ + Log.RingBufferIndexLoadProofGoal step goalIdx numTotalGoals a + Log.PointerArgumentLoadAssertion (Log.SomeSome a) -> + Log.sayCopilot $ + Log.PointerArgumentLoadProofGoal step goalIdx numTotalGoals a + Log.AccessorFunctionLoadAssertion a -> + Log.sayCopilot $ + Log.AccessorFunctionLoadProofGoal step goalIdx numTotalGoals a + Log.GuardFunctionLoadAssertion a -> + Log.sayCopilot $ + Log.GuardFunctionLoadProofGoal step goalIdx numTotalGoals a + Log.UnknownFunctionLoadAssertion a -> + Log.sayCopilot $ + Log.UnknownFunctionLoadProofGoal step goalIdx numTotalGoals a + Log.LLVMBadBehaviorCheckAssertion a -> + Log.sayCopilot $ + Log.LLVMBadBehaviorCheckProofGoal step goalIdx numTotalGoals a + + -- Finally, return the current goal index. + pure goalIdx + + ProveConj gs1 gs2 -> do + goalIdx' <- go goalIdx gs1 + go (goalIdx' + 1) gs2 + +-- | If a verifier assertion does not have a corresponding 'BoolAnn', then we +-- must use heuristics to guess what kind of assertion it is. These heuristics +-- are not perfect, and we fall back to 'Log.UnknownFunctionLoad' in the event +-- that we cannot figure out a more obvious cause for the assertion. +verifierAssertionHeuristics :: + IsSymInterface sym => + sym -> + ProgramLoc -> + Pred sym -> + Log.VerifierAssertion sym +verifierAssertionHeuristics sym loc p + | "_get" `Text.isSuffixOf` functionName funName + = Log.AccessorFunctionLoadAssertion $ + Log.AccessorFunctionLoad sym loc funName p + + | "_guard" `Text.isSuffixOf` functionName funName + = Log.GuardFunctionLoadAssertion $ + Log.GuardFunctionLoad sym loc funName p + + | otherwise + = Log.UnknownFunctionLoadAssertion $ + Log.UnknownFunctionLoad sym loc funName p + where + funName = plFunction loc + +-- | Pick the most recent 'ProgramLoc' in a trace of locations. If there are +-- no locations available, return a dummy location. +nearestProgramLoc :: [ProgramLoc] -> ProgramLoc +nearestProgramLoc locs = + case locs of + loc:_ -> loc + _ -> mkProgramLoc "<>" InternalPos + +-- | A collection of 'IORef's used to accumulate log messages that the verifier +-- may display at the end of verification. +data CopilotLogRefs sym = CopilotLogRefs + { verifierAssertionMapRef :: !(IORef (Map.Map (BoolAnn sym) (Log.VerifierAssertion sym))) + -- ^ A map of 'BoolAnn's (i.e., unique numbers) to 'Log.VerifierAssertions'. + , llvmAnnMapRef :: !(IORef (LLVMAnnMap sym)) + -- ^ A map of 'BoolAnn's (i.e., unique numbers) to assertions about checks + -- for bad behavior in LLVM. + + -- This is kept in a separate 'IORef' for technical reasons, as + -- @crucible-llvm@'s 'explainFailure' function expects this 'IORef' as an + -- argument. We could put everything into 'verifierAssertionMapRef', but + -- that would require some tiresome 'IORef' massaging to make work. + } + +-- | Create a new 'CopilotLogRefs' value. +newCopilotLogRefs :: IsSymInterface sym => IO (CopilotLogRefs sym) +newCopilotLogRefs = do + vaMapRef <- newIORef mempty + laMapRef <- newIORef mempty + pure $ CopilotLogRefs + { verifierAssertionMapRef = vaMapRef + , llvmAnnMapRef = laMapRef + } + +data CopilotLogging + = LoggingCrux Log.CruxLogMessage + | LoggingCruxLLVM Log.CruxLLVMLogMessage + | LoggingCopilot Log.CopilotLogMessage + deriving stock Generic + deriving anyclass ToJSON + +copilotLoggingToSayWhat :: CopilotLogging -> Log.SayWhat +copilotLoggingToSayWhat (LoggingCrux msg) = Log.cruxLogMessageToSayWhat msg +copilotLoggingToSayWhat (LoggingCruxLLVM msg) = Log.cruxLLVMLogMessageToSayWhat msg +copilotLoggingToSayWhat (LoggingCopilot msg) = Log.copilotLogMessageToSayWhat msg + +withCopilotLogging :: + ( ( Log.SupportsCruxLogMessage CopilotLogging + , Log.SupportsCruxLLVMLogMessage CopilotLogging + , Log.SupportsCopilotLogMessage CopilotLogging + ) => computation ) -> + computation +withCopilotLogging computation = do + let ?injectCruxLogMessage = LoggingCrux + ?injectCruxLLVMLogMessage = LoggingCruxLLVM + ?injectCopilotLogMessage = LoggingCopilot + in computation + +sayTranslationWarning :: + Log.Logs msgs => + Log.SupportsCruxLLVMLogMessage msgs => + LLVMTranslationWarning -> IO () +sayTranslationWarning = Log.sayCruxLLVM . f + where + f (LLVMTranslationWarning s p msg) = + Log.TranslationWarning (Text.pack (show (ppSymbol s))) (Text.pack (show p)) msg diff --git a/copilot-verifier/src/Copilot/Verifier/FloatMode.hs b/copilot-verifier/src/Copilot/Verifier/FloatMode.hs new file mode 100644 index 000000000..7d2f62786 --- /dev/null +++ b/copilot-verifier/src/Copilot/Verifier/FloatMode.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} + +module Copilot.Verifier.FloatMode + ( FloatMode(..) + , withFloatMode + , withInterpretedFloatExprBuilder + ) where + +import qualified What4.Expr.Builder as W4 +import qualified What4.InterpretedFloatingPoint as W4 + +-- | How the verifier should interpret floating-point operations. +data FloatMode + = -- | Floating-point values are treated as bitvectors of the appropriate + -- width, and all operations on them are translated as uninterpreted + -- functions. This is the verifier's default interpretation, and it is what + -- allows the verifier to perform any reasoning at all over floating-point + -- operations that are not native to SMT-LIB (@sin@, @cos@, @tan@, etc.). + FloatUninterpreted + + | -- | Floating-point values are treated as bit-precise IEEE-754 floats. This + -- interpretation can perform deeper reasoning about floating-point + -- operations that are native to SMT-LIB, but at the expense of causing the + -- verifier to throw an error if it encounters operations that are not + -- native to SMT-LIB (@sin@, @cos@, @tan@, etc.). Use at your own risk. + FloatIEEE + deriving Show + +-- | Convert a 'FloatMode' into a What4 'W4.FloatModeRepr' and pass it to a +-- continuation. +withFloatMode :: + FloatMode -> + (forall fm. W4.FloatModeRepr fm -> r) -> + r +withFloatMode fm k = + case fm of + FloatUninterpreted -> + k W4.FloatUninterpretedRepr + FloatIEEE -> + k W4.FloatIEEERepr + +-- | Match on a 'W4.FloatModeRepr', compute evidence that it gives rise to an +-- instance of 'W4.IsInterpretedFloatExprBuilder', and pass the evidence to a +-- continutation. +withInterpretedFloatExprBuilder :: + W4.ExprBuilder t st (W4.Flags fm) -> + W4.FloatModeRepr fm -> + (W4.IsInterpretedFloatExprBuilder (W4.ExprBuilder t st (W4.Flags fm)) => r) -> + r +withInterpretedFloatExprBuilder _sym fm k = + case fm of + W4.FloatUninterpretedRepr -> k + W4.FloatIEEERepr -> k + W4.FloatRealRepr -> k diff --git a/copilot-verifier/src/Copilot/Verifier/Log.hs b/copilot-verifier/src/Copilot/Verifier/Log.hs new file mode 100644 index 000000000..0f34711a7 --- /dev/null +++ b/copilot-verifier/src/Copilot/Verifier/Log.hs @@ -0,0 +1,834 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE TemplateHaskell #-} +module Copilot.Verifier.Log + ( SupportsCopilotLogMessage + , CopilotLogMessage(..) + , VerificationStep(..) + , StateRelationStep(..) + , VerifierAssertion(..) + , SomeSome(..) + , StreamValueEquality(..) + , TriggersInvokedCorrespondingly(..) + , TriggerArgumentEquality(..) + , RingBufferLoad(..) + , RingBufferIndexLoad(..) + , PointerArgumentLoad(..) + , AccessorFunctionLoad(..) + , GuardFunctionLoad(..) + , UnknownFunctionLoad(..) + , LLVMBadBehaviorCheck(..) + , sayCopilot + , copilotLogMessageToSayWhat + ) where + +import Crux (SayLevel (..), SayWhat (..)) +import qualified Crux.Log as Log +import Data.Aeson (ToJSON (..), Value (..)) +import Data.Aeson.TH (defaultOptions, deriveToJSON) +import Data.Kind (Type) +import qualified Data.Parameterized.Context as Ctx +import qualified Data.Parameterized.Vector as PV +import qualified Data.Parameterized.TraversableFC.WithIndex as PWI +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Vector as V +import GHC.Generics (Generic) +import qualified Prettyprinter as PP +import qualified Prettyprinter.Render.Text as PP + +import qualified Copilot.Core.Expr as CE +import qualified Copilot.Core.Type as CT +import qualified Copilot.Theorem.What4 as CW4 + +import qualified Lang.Crucible.Simulator as LCS +import qualified Lang.Crucible.Types as LCT +import qualified Lang.Crucible.LLVM.Errors as LCLE +import qualified Lang.Crucible.LLVM.Errors.MemoryError as LCLEME +import qualified Lang.Crucible.LLVM.Errors.UndefinedBehavior as LCLEUB +import qualified Lang.Crucible.LLVM.MemModel as LCLM +import qualified Lang.Crucible.LLVM.MemModel.CallStack as LCLMCS +import qualified What4.FunctionName as WF +import qualified What4.Interface as WI +import qualified What4.ProgramLoc as WPL + +data CopilotLogMessage where + GeneratedCFile :: + FilePath + -- ^ The path of the generated C File + -> CopilotLogMessage + CompiledBitcodeFile :: + String + -- ^ The prefix to use in the compiled bitcode's directory + -> FilePath + -- ^ The name of the generated LLVM bitcode file + -> CopilotLogMessage + TranslatedToCrucible :: CopilotLogMessage + GeneratingProofState :: CopilotLogMessage + ComputingConditions :: VerificationStep -> CopilotLogMessage + ProvingConditions :: VerificationStep -> CopilotLogMessage + AllGoalsProved :: CopilotLogMessage + OnlySomeGoalsProved :: + Integer + -- ^ Number of goals proved + -> Integer + -- ^ Number of total goals + -> CopilotLogMessage + SuccessfulProofSummary :: + FilePath + -- ^ Name of the generated C file. + -> Integer + -- ^ Number of goals proven during for the initial state of the proof. + -> Integer + -- ^ Number of goals proven during the bisimulation step of the proof. + -> CopilotLogMessage + NoisyVerbositySuggestion :: CopilotLogMessage + + ----- + -- Types of proof goals the verifier emits + -- + -- The first three arguments to each constructor are: + -- + -- * Which step of the verifier we are on + -- * The current goal number (zero-indexed) + -- * The total number of goals + ----- + + StreamValueEqualityProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> StreamValueEquality sym copilotType crucibleType + -> CopilotLogMessage + + TriggersInvokedCorrespondinglyProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> TriggersInvokedCorrespondingly sym + -> CopilotLogMessage + + TriggerArgumentEqualityProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> TriggerArgumentEquality sym copilotType crucibleType + -> CopilotLogMessage + + RingBufferLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> RingBufferLoad sym copilotType crucibleType + -> CopilotLogMessage + + RingBufferIndexLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> RingBufferIndexLoad sym + -> CopilotLogMessage + + PointerArgumentLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> PointerArgumentLoad sym copilotType crucibleType + -> CopilotLogMessage + + AccessorFunctionLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> AccessorFunctionLoad sym + -> CopilotLogMessage + + GuardFunctionLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> GuardFunctionLoad sym + -> CopilotLogMessage + + UnknownFunctionLoadProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> UnknownFunctionLoad sym + -> CopilotLogMessage + + LLVMBadBehaviorCheckProofGoal :: + WI.IsSymExprBuilder sym + => VerificationStep + -> Integer + -> Integer + -> LLVMBadBehaviorCheck sym + -> CopilotLogMessage + +data VerificationStep + = InitialState + | StepBisimulation + deriving stock Generic + deriving anyclass ToJSON + +-- | The provenance of an assertion. +data AssertionProvenance + = VerifierProvenance + -- ^ Assertions that the verifier directly makes to check the correspondence + -- between the Copilot specification and the generated C code. Because these + -- assertions are core to the proof that the verifier is computing, these + -- assertions' proof goals are always logged, even if they are trivial. + | StepExecutionProvenance + -- ^ Assertions that arise during symbolic execution of the @step()@ + -- function as part of proving the memory safety of the generated C code. + -- These assertions' proof goals are only logged if they are non-trivial, + -- as there would be an excessive number of these proof goals otherwise. + +-- | At what step of the proof are we checking the state relation? We record +-- this so that we can better distinguish between transition step–related +-- proof goals that arise before or after calling the @step()@ function. +data StateRelationStep + = InitialStateRelation + -- ^ Check the state relation for the initial state. + | PreStepStateRelation + -- ^ During the transition step of the proof, check the state relation + -- before calling the @step()@ function. + | PostStepStateRelation + -- ^ During the transition step of the proof, check the state relation + -- after calling the @step()@ function. + deriving stock Generic + deriving anyclass ToJSON + +-- | Types of assertions that the verifier can make, which will count towards +-- the total number of proof goals. +data VerifierAssertion sym + = StreamValueEqualityAssertion (SomeSome (StreamValueEquality sym)) + | TriggersInvokedCorrespondinglyAssertion (TriggersInvokedCorrespondingly sym) + | TriggerArgumentEqualityAssertion (SomeSome (TriggerArgumentEquality sym)) + | RingBufferLoadAssertion (SomeSome (RingBufferLoad sym)) + | RingBufferIndexLoadAssertion (RingBufferIndexLoad sym) + | PointerArgumentLoadAssertion (SomeSome (PointerArgumentLoad sym)) + | AccessorFunctionLoadAssertion (AccessorFunctionLoad sym) + | GuardFunctionLoadAssertion (GuardFunctionLoad sym) + | UnknownFunctionLoadAssertion (UnknownFunctionLoad sym) + | LLVMBadBehaviorCheckAssertion (LLVMBadBehaviorCheck sym) + +-- | Like @Some@ in @parameterized-utils@, but existentially closing over two +-- type parameters instead of just one. +data SomeSome (f :: j -> k -> Type) where + SomeSome :: f x y -> SomeSome f + +-- | An assertion that an element in a Copilot stream is equal to the +-- corresponding element in a C ring buffer. +data StreamValueEquality sym copilotType crucibleType where + StreamValueEquality :: + sym + -> StateRelationStep + -- ^ When the values are checked for equality + -> WPL.ProgramLoc + -- ^ The locations of the values + -> Text + -- ^ The name of the buffer + -> Integer + -- ^ The offset from the buffer's index, which is used to compute the + -- element of the buffer to load + -> Integer + -- ^ The number of elements in the buffer + -> CT.Type copilotType + -- ^ The Copilot type + -> CW4.XExpr sym + -- ^ The Copilot value + -> LCT.TypeRepr crucibleType + -- ^ The Crucible type + -> LCS.RegValue sym crucibleType + -- ^ The Crucible value + -> StreamValueEquality sym copilotType crucibleType + +-- | An assertion that, given a Copilot trigger stream and its corresponding C +-- trigger function on a particular time step, either both fired at the same +-- time or both did not fire at all. +data TriggersInvokedCorrespondingly sym where + TriggersInvokedCorrespondingly :: + WPL.ProgramLoc + -- ^ The location of the trigger + -> CE.Name + -- ^ The trigger name + -> WI.SymNat sym + -- ^ The expected number of times the trigger was fired this step + -- (should be either 1 or 0). + -> WI.SymNat sym + -- ^ The actual number of times the trigger was fired this step. + -> TriggersInvokedCorrespondingly sym + +-- | An assertion that an argument to a Copilot trigger is equal to the +-- corresponding argument to a C trigger function. +data TriggerArgumentEquality sym copilotType crucibleType where + TriggerArgumentEquality :: + sym + -> WPL.ProgramLoc + -- ^ The locations of the arguments + -> CE.Name + -- ^ The trigger name + -> Integer + -- ^ The number of the argument (starting from 0) + -> CT.Type copilotType + -- ^ The Copilot type + -> CW4.XExpr sym + -- ^ The Copilot value + -> LCT.TypeRepr crucibleType + -- ^ The Crucible type + -> LCS.RegValue sym crucibleType + -- ^ The Crucible value + -> TriggerArgumentEquality sym copilotType crucibleType + +-- | An assertion that a load from a ring buffer in C is valid. +data RingBufferLoad sym copilotType crucibleType where + RingBufferLoad :: + sym + -> StateRelationStep + -- ^ When the ring buffer is loaded from + -> WPL.ProgramLoc + -- ^ The location of the buffer + -> Text + -- ^ The name of the buffer + -> Integer + -- ^ The offset from the buffer's index, which is used to compute the + -- element of the buffer to load + -> Integer + -- ^ The number of elements in the buffer + -> CT.Type copilotType + -- ^ The Copilot type of the elements of the array + -> LCT.TypeRepr crucibleType + -- ^ The Crucible type of the elements of the array + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> RingBufferLoad sym copilotType crucibleType + +-- | An assertion that a load from a global variable representing a ring +-- buffer's index in C is valid. +data RingBufferIndexLoad sym where + RingBufferIndexLoad :: + sym + -> StateRelationStep + -- ^ When the index's global variable is loaded from + -> WPL.ProgramLoc + -- ^ The location of the global index + -> Text + -- ^ The name of the global index + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> RingBufferIndexLoad sym + +-- | An assertion that a load from a pointer argument to a trigger function in C +-- is valid. +data PointerArgumentLoad sym copilotType crucibleType where + PointerArgumentLoad :: + sym + -> WPL.ProgramLoc + -- ^ The location of the pointer + -> CT.Type copilotType + -- ^ The Copilot type of the underlying memory + -> LCT.TypeRepr crucibleType + -- ^ The Crucible type of the underlying memory + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> PointerArgumentLoad sym copilotType crucibleType + +-- | An assertion that a load occurring from somewhere in a stream accessor +-- function in C (e.g., @s0_get@) is valid. This is a somewhat imprecise +-- assertion, as it doesn't identify /why/ the load occurs. (Most likely, it +-- happens because of an array index.) +data AccessorFunctionLoad sym where + AccessorFunctionLoad :: + sym + -> WPL.ProgramLoc + -- ^ The location of the accessor function + -> WF.FunctionName + -- ^ The name of the accessor function + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> AccessorFunctionLoad sym + +-- | An assertion that a load occurring from somewhere in a trigger guard +-- function in C (e.g., @even_guard@) is valid. This is a somewhat imprecise +-- assertion, as it doesn't identify /why/ the load occurs. (Most likely, it +-- happens because of an array index.) +data GuardFunctionLoad sym where + GuardFunctionLoad :: + sym + -> WPL.ProgramLoc + -- ^ The location of the guard function + -> WF.FunctionName + -- ^ The name of the guard function + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> GuardFunctionLoad sym + +-- | An assertion that a load occurring in some function is valid. If you +-- see this assertion, it is because the heuristics used to identify where +-- load-related assertions come from could not identify a more precise cause +-- for a load. +data UnknownFunctionLoad sym where + UnknownFunctionLoad :: + sym + -> WPL.ProgramLoc + -- ^ The location of the function + -> WF.FunctionName + -- ^ The name of the function + -> WI.Pred sym + -- ^ The assertion that must hold in order for this load to be valid + -> UnknownFunctionLoad sym + +-- | An assertion that checks that some form of bad behavior in LLVM does not +-- occur. Bad behavior includes both undefined behavior and memory unsafety. +data LLVMBadBehaviorCheck sym where + LLVMBadBehaviorCheck :: + sym + -> WPL.ProgramLoc + -- ^ The location of the check + -> LCLMCS.CallStack + -- ^ A call stack for the check, if one exists + -> LCLE.BadBehavior sym + -- ^ What type of LLVM bad behavior is being checked for + -> WI.Pred sym + -- ^ The assertion that must hold in order for this check to succeed + -> LLVMBadBehaviorCheck sym + +-- Silly ToJSON instances. Crux is only requiring a ToJSON constraint for +-- IDE-related functionality that we do not make use of, so the behavior of +-- these instances aren't very important. + +instance ToJSON (StreamValueEquality sym copilotType crucibleType) where + toJSON _ = String "StreamValueEquality" + +instance ToJSON (TriggersInvokedCorrespondingly sym) where + toJSON _ = String "TriggersInvokedCorrespondingly" + +instance ToJSON (TriggerArgumentEquality sym copilotType crucibleType) where + toJSON _ = String "TriggerArgumentEquality" + +instance ToJSON (RingBufferLoad sym copilotType crucibleType) where + toJSON _ = String "RingBufferLoad" + +instance ToJSON (RingBufferIndexLoad sym) where + toJSON _ = String "RingBufferIndexLoad" + +instance ToJSON (PointerArgumentLoad sym copilotType crucibleType) where + toJSON _ = String "PointerArgumentLoad" + +instance ToJSON (AccessorFunctionLoad sym) where + toJSON _ = String "AccessorFunctionLoad" + +instance ToJSON (GuardFunctionLoad sym) where + toJSON _ = String "GuardFunctionLoad" + +instance ToJSON (UnknownFunctionLoad sym) where + toJSON _ = String "UnknownFunctionLoad" + +instance ToJSON (LLVMBadBehaviorCheck sym) where + toJSON _ = String "LLVMBadBehaviorCheck" + +type SupportsCopilotLogMessage msgs = + (?injectCopilotLogMessage :: CopilotLogMessage -> msgs) + +sayCopilot :: + Log.Logs msgs => + SupportsCopilotLogMessage msgs => + CopilotLogMessage -> + IO () +sayCopilot msg = + let ?injectMessage = ?injectCopilotLogMessage + in Log.say msg + +copilotTag :: Text +copilotTag = "copilot-verifier" + +-- copilotFail :: Text -> SayWhat +-- copilotFail = SayWhat Fail copilotTag + +copilotSimply :: Text -> SayWhat +copilotSimply = SayWhat Simply copilotTag + +copilotNoisily :: Text -> SayWhat +copilotNoisily = SayWhat Noisily copilotTag + +-- copilotWarn :: Text -> SayWhat +-- copilotWarn = SayWhat Warn copilotTag + +copilotLogMessageToSayWhat :: CopilotLogMessage -> SayWhat +copilotLogMessageToSayWhat (GeneratedCFile csrc) = + copilotSimply $ "Generated " <> T.pack (show csrc) +copilotLogMessageToSayWhat (CompiledBitcodeFile prefix bcFile) = + copilotSimply $ "Compiled " <> T.pack prefix <> " into " <> T.pack bcFile +copilotLogMessageToSayWhat TranslatedToCrucible = + copilotSimply "Translated bitcode into Crucible" +copilotLogMessageToSayWhat GeneratingProofState = + copilotSimply "Generating proof state data" +copilotLogMessageToSayWhat (ComputingConditions step) = + copilotSimply $ "Computing " <> describeVerificationStep step <> " verification conditions" +copilotLogMessageToSayWhat (ProvingConditions step) = + copilotSimply $ "Proving " <> describeVerificationStep step <> " verification conditions" +copilotLogMessageToSayWhat AllGoalsProved = + copilotSimply "All obligations proved by concrete simplification" +copilotLogMessageToSayWhat (OnlySomeGoalsProved numProvedGoals numTotalGoals) = + copilotSimply $ T.unwords + [ "Proved", T.pack (show numProvedGoals) + , "of" + , T.pack (show numTotalGoals), "total goals" + ] +copilotLogMessageToSayWhat (SuccessfulProofSummary cFileName initGoals bisimGoals) = + copilotSimply $ T.unlines + [ "" + , "copilot-verifier has produced a mathematical proof that the behavior of" + , "the generated C program (" <> T.pack cFileName + <> ") precisely matches the behavior of" + , "the Copilot specification. This proof shows that the behaviors match for" + , "all possible moments in time, and in doing so, copilot-verifier examined" + , "how the programs behave at the start of execution (the \"initial state\")" + , "and at an arbitrary point of time in execution (the \"step bisimulation\")." + , "copilot-verifier decomposed the overall proof into smaller goals, the" + , "number of which depends on the size and complexity of the program. In this" + , "example, there are " <> T.pack (show initGoals) <> " initial state goal(s)" + <> " and " <> T.pack (show bisimGoals) <> " step bisimulation goal(s)," + , "and copilot-verifier was able to prove all of them." + ] +copilotLogMessageToSayWhat NoisyVerbositySuggestion = + copilotSimply $ T.unlines + [ "" + , "copilot-verifier is displaying an abridged summary of the verification results." + , "copilot-verifier also includes a \"noisy\" mode that prints detailed" + , "descriptions of each individual proof goal, including what type of goal it is," + , "why it needs to be proven, and where in the Copilot specification and/or C code" + , "the proof goal originated from. To enable noisy mode, invoke the verifier with" + , "the following options:" + , "" + , "```" + , "verifyWithOptions (defaultVerifierOptions { verbosity = Noisy }) ..." + , "```" + ] +copilotLogMessageToSayWhat + (StreamValueEqualityProofGoal verifStep goalIdx numTotalGoals + (StreamValueEquality + sym stateRelStep loc + bufName offset len + copilotTy copilotVal + crucibleTy crucibleVal)) = + copilotNoisily $ + displayStateRelationProofGoal + verifStep stateRelStep VerifierProvenance goalIdx numTotalGoals + "asserting the equality between two stream values" + [ renderStrict $ ppProgramLoc loc + , "* Ring buffer name: " <> bufName + , "* Offset into buffer (from current index): " <> T.pack (show offset) + , "* Number of elements in buffer: " <> T.pack (show len) + , "* Copilot type: " <> T.pack (showsCopilotType 0 copilotTy "") + , "* Copilot value:" + , renderStrict $ PP.indent 4 $ ppCopilotValue copilotVal + , "* Crucible value:" + , renderStrict $ PP.indent 4 $ ppCrucibleValue sym crucibleTy crucibleVal + ] +copilotLogMessageToSayWhat + (TriggersInvokedCorrespondinglyProofGoal step goalIdx numTotalGoals + (TriggersInvokedCorrespondingly loc name expected actual)) = + copilotNoisily $ + displayProofGoal + step VerifierProvenance goalIdx numTotalGoals + "asserting triggers fired in corresponding ways" + [ renderStrict $ ppProgramLoc loc + , "* Trigger name: " <> T.pack name + , "* Expected number of times trigger was fired:" + , renderStrict $ PP.indent 4 $ WI.printSymNat expected + , "* Actual number of times trigger was fired:" + , renderStrict $ PP.indent 4 $ WI.printSymNat actual + ] +copilotLogMessageToSayWhat + (TriggerArgumentEqualityProofGoal step goalIdx numTotalGoals + (TriggerArgumentEquality + sym loc + triggerName argNum + copilotTy copilotVal + crucibleTy crucibleVal)) = + copilotNoisily $ + displayProofGoal + step VerifierProvenance goalIdx numTotalGoals + "asserting the equality between two trigger arguments" + [ renderStrict $ ppProgramLoc loc + , "* Trigger name: " <> T.pack triggerName + , "* Number of argument: " <> T.pack (show argNum) + , "* Copilot type: " <> T.pack (showsCopilotType 0 copilotTy "") + , "* Copilot value:" + , renderStrict $ PP.indent 4 $ ppCopilotValue copilotVal + , "* Crucible value:" + , renderStrict $ PP.indent 4 $ ppCrucibleValue sym crucibleTy crucibleVal + ] +copilotLogMessageToSayWhat + (RingBufferLoadProofGoal verifStep goalIdx numTotalGoals + (RingBufferLoad + _sym stateRelStep loc bufName offset len copilotTy _crucibleTy p)) = + copilotNoisily $ + displayStateRelationProofGoal + verifStep stateRelStep VerifierProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from a stream's ring buffer in C" + [ renderStrict $ ppProgramLoc loc + , "* Ring buffer name: " <> bufName + , "* Offset into buffer (from current index): " <> T.pack (show offset) + , "* Number of elements in buffer: " <> T.pack (show len) + , "* Copilot type of buffer elements:" <> T.pack (showsCopilotType 0 copilotTy "") + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (RingBufferIndexLoadProofGoal verifStep goalIdx numTotalGoals + (RingBufferIndexLoad _sym stateRelStep loc idxName p)) = + copilotNoisily $ + displayStateRelationProofGoal + verifStep stateRelStep VerifierProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from the index to a stream's ring buffer in C" + [ renderStrict $ ppProgramLoc loc + , "* Ring buffer index name: " <> idxName + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (PointerArgumentLoadProofGoal step goalIdx numTotalGoals + (PointerArgumentLoad + _sym loc copilotTy _crucibleTy p)) = + copilotNoisily $ + displayProofGoal + step VerifierProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from a pointer argument to a trigger" + [ renderStrict $ ppProgramLoc loc + , "* Copilot type: " <> T.pack (showsCopilotType 0 copilotTy "") + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (AccessorFunctionLoadProofGoal step goalIdx numTotalGoals + (AccessorFunctionLoad _sym loc accessorName p)) = + copilotNoisily $ + displayProofGoal + step StepExecutionProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from a stream accessor function" + [ renderStrict $ ppProgramLoc loc + , "* Accessor function name: " <> WF.functionName accessorName + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (GuardFunctionLoadProofGoal step goalIdx numTotalGoals + (GuardFunctionLoad _sym loc accessorName p)) = + copilotNoisily $ + displayProofGoal + step StepExecutionProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from a trigger guard function" + [ renderStrict $ ppProgramLoc loc + , "* Guard function name: " <> WF.functionName accessorName + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (UnknownFunctionLoadProofGoal step goalIdx numTotalGoals + (UnknownFunctionLoad _sym loc accessorName p)) = + copilotNoisily $ + displayProofGoal + step StepExecutionProvenance goalIdx numTotalGoals + "asserting the validity of a memory load from an unknown function" + [ renderStrict $ ppProgramLoc loc + , "* Function name: " <> WF.functionName accessorName + , "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] +copilotLogMessageToSayWhat + (LLVMBadBehaviorCheckProofGoal step goalIdx numTotalGoals + (LLVMBadBehaviorCheck _sym loc stk bb p)) = + let ppLoc = renderStrict $ ppProgramLoc loc + ppCallStackLines = + [ "* Call stack:" + , " " <> renderCallStack stk + ] + ppValidPredLines = + [ "* Validity predicate:" + , renderStrict $ PP.indent 4 $ WI.printSymExpr p + ] in + case bb of + LCLE.BBUndefinedBehavior ub -> + copilotNoisily $ + displayProofGoal + step StepExecutionProvenance goalIdx numTotalGoals + "asserting that LLVM undefined behavior does not occur" + $ ppLoc : ppCallStackLines ++ + [ "* Undefined behavior description:" + , renderStrict $ PP.indent 4 $ LCLEUB.ppDetails ub + ] ++ ppValidPredLines + LCLE.BBMemoryError me -> + copilotNoisily $ + displayProofGoal + step StepExecutionProvenance goalIdx numTotalGoals + "asserting that LLVM memory unsafety does not occur" + $ ppLoc : ppCallStackLines ++ + [ "* Memory unsafety description:" + , renderStrict $ PP.indent 4 $ LCLEME.explain me + ] ++ ppValidPredLines + +describeVerificationStep :: VerificationStep -> Text +describeVerificationStep InitialState = "initial state" +describeVerificationStep StepBisimulation = "step bisimulation" + +-- | Display information about an emitted proof goal. +displayProofGoal :: + VerificationStep + -> AssertionProvenance + -> Integer + -> Integer + -> Text + -> [Text] + -> Text +displayProofGoal step assertProv goalIdx numTotalGoals why ls = T.unlines $ + [ banner + , "Emitted a proof goal (" <> why <> ")" + , " During the " <> displayStep + , " Required for proving the " <> displayAssertProv + , " Proof goal " <> T.pack (show goalIdx) + <> " (" + <> T.pack (show numTotalGoals) + <> " total)" + , "" + ] + ++ ls ++ [banner] + where + banner = "=====" + displayStep = + case step of + InitialState -> + "initial bisimulation state step" + StepBisimulation -> + "transition step of bisimulation" + displayAssertProv = + case assertProv of + VerifierProvenance -> + "correspondence between the spec and C code" + StepExecutionProvenance -> + "memory safety of the generated step() function" + +-- | Display information about an emitted proof goal that involves checking the +-- state relation. +displayStateRelationProofGoal :: + VerificationStep + -> StateRelationStep + -> AssertionProvenance + -> Integer + -> Integer + -> Text + -> [Text] + -> Text +displayStateRelationProofGoal verifStep stateRelStep assertProv goalIdx numTotalGoals why = + displayProofGoal verifStep assertProv goalIdx numTotalGoals why' + where + why' :: Text + why' = + case stateRelStep of + PreStepStateRelation -> why <> " before calling step()" + PostStepStateRelation -> why <> " after calling step()" + -- `displayProofGoal` already makes a note of the fact that we're + -- checking the initial state, so no need to do so again here. + InitialStateRelation -> why + +ppProgramLoc :: WPL.ProgramLoc -> PP.Doc a +ppProgramLoc pl = PP.vcat + [ "* Function:" PP.<+> PP.pretty (WPL.plFunction pl) + , " Position:" PP.<+> PP.pretty (WPL.plSourceLoc pl) + ] + +renderCallStack :: LCLMCS.CallStack -> Text +renderCallStack cs + | T.null ppText + = "" + | otherwise + = ppText + where + ppText = renderStrict $ LCLMCS.ppCallStack cs + +showsCopilotType :: Int -> CT.Type tp -> ShowS +showsCopilotType prec tp = + case tp of + CT.Bool -> showString "Bool" + CT.Int8 -> showString "Int8" + CT.Int16 -> showString "Int16" + CT.Int32 -> showString "Int32" + CT.Int64 -> showString "Int64" + CT.Word8 -> showString "Word8" + CT.Word16 -> showString "Word16" + CT.Word32 -> showString "Word32" + CT.Word64 -> showString "Word64" + CT.Float -> showString "Float" + CT.Double -> showString "Double" + CT.Array t -> showParen (prec >= 11) $ + showString "Array" . + showsPrec 11 (CT.typeSize tp) . + showsCopilotType 11 t + CT.Struct x -> showString $ CT.typeName x + +ppCopilotValue :: WI.IsSymExprBuilder sym => CW4.XExpr sym -> PP.Doc a +ppCopilotValue val = + case val of + CW4.XBool b -> WI.printSymExpr b + CW4.XInt8 i -> WI.printSymExpr i + CW4.XInt16 i -> WI.printSymExpr i + CW4.XInt32 i -> WI.printSymExpr i + CW4.XInt64 i -> WI.printSymExpr i + CW4.XWord8 w -> WI.printSymExpr w + CW4.XWord16 w -> WI.printSymExpr w + CW4.XWord32 w -> WI.printSymExpr w + CW4.XWord64 w -> WI.printSymExpr w + CW4.XFloat f -> WI.printSymExpr f + CW4.XDouble d -> WI.printSymExpr d + CW4.XEmptyArray {} -> "[]" + CW4.XArray a -> ppBracesWith ppCopilotValue (PV.toList a) + CW4.XStruct s -> ppBracketsWith ppCopilotValue s + +ppCrucibleValue :: WI.IsSymExprBuilder sym + => sym + -> LCT.TypeRepr tp + -> LCS.RegValue sym tp + -> PP.Doc a +ppCrucibleValue sym tp val = + case tp of + LCLM.LLVMPointerRepr _ -> LCLM.ppPtr val + LCT.FloatRepr _ -> WI.printSymExpr val + LCT.VectorRepr tpr -> ppBracketsWith (ppCrucibleValue sym tpr) (V.toList val) + LCT.StructRepr ctx -> withBraces $ + PWI.itoListFC (\i (LCS.RV v) -> ppCrucibleValue sym (ctx Ctx.! i) v) val + _ -> error $ "ppCrucibleValue: Unsupported type: " ++ show tp + +renderStrict :: PP.Doc a -> Text +renderStrict = PP.renderStrict . PP.layoutPretty PP.defaultLayoutOptions + +ppBracketsWith :: (a -> PP.Doc b) -> [a] -> PP.Doc b +ppBracketsWith f = PP.align . PP.list . map f + +ppBracesWith :: (a -> PP.Doc b) -> [a] -> PP.Doc b +ppBracesWith f = withBraces . map f + +withBraces :: [PP.Doc a] -> PP.Doc a +withBraces = + PP.align + . PP.encloseSep (PP.flatAlt "{ " "{") (PP.flatAlt " }" "}") ", " + +$(deriveToJSON defaultOptions ''CopilotLogMessage) diff --git a/copilot-verifier/src/Copilot/Verifier/Solver.hs b/copilot-verifier/src/Copilot/Verifier/Solver.hs new file mode 100644 index 000000000..8887be811 --- /dev/null +++ b/copilot-verifier/src/Copilot/Verifier/Solver.hs @@ -0,0 +1,25 @@ +module Copilot.Verifier.Solver + ( Solver(..) + , solverAdapter + ) where + +import qualified What4.Solver.Adapter as W4 +import qualified What4.Solver.CVC4 as W4.CVC4 +import qualified What4.Solver.CVC5 as W4.CVC5 +import qualified What4.Solver.Yices as W4.Yices +import qualified What4.Solver.Z3 as W4.Z3 + +-- | General-purpose SMT solvers that @copilot-verifier@ supports. +data Solver + = CVC4 + | CVC5 + | Yices + | Z3 + deriving Show + +-- | Return the @what4@ 'W4.SolverAdapter' corresponding to a given 'Solver'. +solverAdapter :: Solver -> W4.SolverAdapter st +solverAdapter CVC4 = W4.CVC4.cvc4Adapter +solverAdapter CVC5 = W4.CVC5.cvc5Adapter +solverAdapter Yices = W4.Yices.yicesAdapter +solverAdapter Z3 = W4.Z3.z3Adapter diff --git a/copilot-verifier/test/Test.hs b/copilot-verifier/test/Test.hs new file mode 100644 index 000000000..8ed51e915 --- /dev/null +++ b/copilot-verifier/test/Test.hs @@ -0,0 +1,32 @@ +module Main (main) where + +import qualified Data.CaseInsensitive as CI +import qualified Data.Map as Map +import qualified Data.Text as Text +import System.IO (stderr, stdout) +import System.IO.Silently (hSilence) +import Test.Tasty +import Test.Tasty.ExpectedFailure +import Test.Tasty.HUnit + +import Copilot.Verifier (Verbosity(..)) +import Copilot.Verifier.Examples (shouldFailExamples, shouldPassExamples) + +main :: IO () +main = defaultMain $ + testGroup "copilot-verifier-examples tests" + [ testGroup "should-fail tests" $ + -- Why use hSilence for the should-fail tests when we are passing + -- Quiet? It's because crux-llvm errors are logged at the highest + -- severity possible, and even Crux's quietMode isn't enough to + -- suppress those messages. We could try messing with things on the + -- Crux side to avoid this, but it's simpler just to use hSilence here. + -- After all, we don't really care about the output of failing tests + -- anyway, just their exit codes. + map (\(name, action) -> expectFail (testCase (Text.unpack (CI.original name)) + (hSilence [stderr, stdout] action))) + (Map.toAscList (shouldFailExamples Quiet)) + , testGroup "should-pass tests" $ + map (\(name, action) -> testCase (Text.unpack (CI.original name)) action) + (Map.toAscList (shouldPassExamples Quiet)) + ] From 1d299905a6c7b5885bc459ed4ab925c7c66145a1 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 20 Jun 2025 10:05:21 -0400 Subject: [PATCH 195/483] copilot-verifier: Adjust issue numbers in changelog. Refs #622. The `copilot-verifier` package was originally developed in an external repo (https://github.com/Copilot-Language/copilot-verifier), but there is interest in having all Copilot projects and libraries merged into the main https://github.com/Copilot-Language/copilot repo for ease of management. Now that the `copilot-verifier` package lives in the `copilot` repo, this adjusts the issue numbers in the `copilot-verifier` changelog to refer to the old repo so that they will not be confused with issues in the `copilot` repo. --- copilot-verifier/CHANGELOG | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 2923ff5ed..4d3f2eacc 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,40 +1,46 @@ 2025-05-08 - * Version bump (4.4). (#85) + * Version bump (4.4). (copilot-verifier#85) 2025-03-10 - * Version bump (4.3). (#82) - * Add `smtSolver` option to `VerifierOptions`. (#78) - * Add `smtFloatMode` option to `VerifierOptions`. (#79) + * Version bump (4.3). (copilot-verifier#82) + * Add `smtSolver` option to `VerifierOptions`. (copilot-verifier#78) + * Add `smtFloatMode` option to `VerifierOptions`. (copilot-verifier#79) 2025-01-20 - * Version bump (4.2). (#76) - * Reject specs that use multiple triggers with the same name. (#74) + * Version bump (4.2). (copilot-verifier#76) + * Reject specs that use multiple triggers with the same name. + (copilot-verifier#74) 2024-11-08 - * Version bump (4.1). (#72) + * Version bump (4.1). (copilot-verifier#72) 2024-09-09 - * Version bump (4.0). (#69) - * Support verifying programs that use array updates. (#63) - * Support verifying programs that use struct updates. (#57) + * Version bump (4.0). (copilot-verifier#69) + * Support verifying programs that use array updates. + (copilot-verifier#63) + * Support verifying programs that use struct updates. + (copilot-verifier#57) 2024-08-03 - * Support building with `crucible-llvm-0.7` and `crux-llvm-0.9`. (#64) - * Support GHC 9.4 through 9.8. (#65) + * Support building with `crucible-llvm-0.7` and `crux-llvm-0.9`. + (copilot-verifier#64) + * Support GHC 9.4 through 9.8. (copilot-verifier#65) 2024-07-30 * When using `Noisy` verbosity, always log proof goals related to the - core correspondence proof, even if the goals are trivial. (#51) + core correspondence proof, even if the goals are trivial. + (copilot-verifier#51) * When using `Noisy` verbosity, log more information about which proof - goals arise before or after calling the `step()` function. (#52) + goals arise before or after calling the `step()` function. + (copilot-verifier#52) 2024-07-11 - * Version bump (3.20). (#58) + * Version bump (3.20). (copilot-verifier#58) 2024-03-08 - * Version bump (3.19). (#53) + * Version bump (3.19). (copilot-verifier#53) * Provide more detailed feedback upon a successful run of the verifier. - * Make the examples build with Copilot 3.19. (#53) + * Make the examples build with Copilot 3.19. (copilot-verifier#53) 2024-02-06 * Initial release of `copilot-verifier`. From 9e0dcac323f523277b1bdcb74869d823b6ad5324 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 20 Jun 2025 10:06:43 -0400 Subject: [PATCH 196/483] copilot-verifier: Document changes in CHANGELOG. Refs #622. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 4d3f2eacc..03bd15246 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2025-06-20 + * Include `copilot-verifier` in mainline `copilot` repo. (#622) + 2025-05-08 * Version bump (4.4). (copilot-verifier#85) From 2230f35b0e258fe3088c7019959606b96d95cd88 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 27 Jun 2025 13:23:58 +0000 Subject: [PATCH 197/483] copilot-visualizer: Include in mainline. Refs #624. The copilot-visualizer package was developed outside of this repo, but there's interest in having all Copilot projects and libraries merged into a main repo for ease of management. This commit adds version 4.4 of copilot-visualizer as a subdirectory of the main repository. All files are added "as is". --- copilot-visualizer/.gitignore | 25 + copilot-visualizer/CHANGELOG | 3 + copilot-visualizer/LICENSE | 29 ++ copilot-visualizer/README.md | 34 ++ copilot-visualizer/Setup.hs | 2 + copilot-visualizer/copilot-visualizer.cabal | 130 +++++ .../data/static_html/index.html | 255 +++++++++ copilot-visualizer/data/tikz/tikz.tex | 37 ++ copilot-visualizer/examples/gui/Main.hs | 20 + copilot-visualizer/examples/gui/copilot.css | 50 ++ copilot-visualizer/examples/gui/copilot.js | 489 ++++++++++++++++++ copilot-visualizer/examples/gui/main.html | 67 +++ copilot-visualizer/examples/tikz/Heater.hs | 37 ++ .../src/Copilot/Visualize/Dynamic.hs | 253 +++++++++ .../src/Copilot/Visualize/Live.hs | 162 ++++++ .../src/Copilot/Visualize/Static.hs | 36 ++ .../src/Copilot/Visualize/TypedTrace.hs | 158 ++++++ .../src/Copilot/Visualize/UntypedTrace.hs | 176 +++++++ 18 files changed, 1963 insertions(+) create mode 100644 copilot-visualizer/.gitignore create mode 100644 copilot-visualizer/CHANGELOG create mode 100644 copilot-visualizer/LICENSE create mode 100644 copilot-visualizer/README.md create mode 100644 copilot-visualizer/Setup.hs create mode 100644 copilot-visualizer/copilot-visualizer.cabal create mode 100644 copilot-visualizer/data/static_html/index.html create mode 100644 copilot-visualizer/data/tikz/tikz.tex create mode 100644 copilot-visualizer/examples/gui/Main.hs create mode 100644 copilot-visualizer/examples/gui/copilot.css create mode 100644 copilot-visualizer/examples/gui/copilot.js create mode 100644 copilot-visualizer/examples/gui/main.html create mode 100644 copilot-visualizer/examples/tikz/Heater.hs create mode 100644 copilot-visualizer/src/Copilot/Visualize/Dynamic.hs create mode 100644 copilot-visualizer/src/Copilot/Visualize/Live.hs create mode 100644 copilot-visualizer/src/Copilot/Visualize/Static.hs create mode 100644 copilot-visualizer/src/Copilot/Visualize/TypedTrace.hs create mode 100644 copilot-visualizer/src/Copilot/Visualize/UntypedTrace.hs diff --git a/copilot-visualizer/.gitignore b/copilot-visualizer/.gitignore new file mode 100644 index 000000000..f94ebc47b --- /dev/null +++ b/copilot-visualizer/.gitignore @@ -0,0 +1,25 @@ +dist +dist-* +cabal-dev +*.o +*.hi +*.chi +*.chs.h +*.dyn_o +*.dyn_hi +.hpc +.hsenv +.cabal-sandbox/ +cabal.sandbox.config +*.prof +*.aux +*.hp +*.eventlog +.stack-work/ +cabal.project.local +cabal.project.local~ +.HTF/ +.ghc.environment.* +copilot-profiling +.DS_Store +.log diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG new file mode 100644 index 000000000..44cb9f05e --- /dev/null +++ b/copilot-visualizer/CHANGELOG @@ -0,0 +1,3 @@ +2025-06-26 + * Release 4.4. (#5) + * Initial version. (#4) diff --git a/copilot-visualizer/LICENSE b/copilot-visualizer/LICENSE new file mode 100644 index 000000000..d3c9e46cd --- /dev/null +++ b/copilot-visualizer/LICENSE @@ -0,0 +1,29 @@ +2024-2025 (c) NASA +BSD3 License terms + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the name of the developers nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/copilot-visualizer/README.md b/copilot-visualizer/README.md new file mode 100644 index 000000000..f4a93c4a2 --- /dev/null +++ b/copilot-visualizer/README.md @@ -0,0 +1,34 @@ +[![Build Status](https://travis-ci.com/Copilot-Language/copilot.svg?branch=master)](https://app.travis-ci.com/github/Copilot-Language/copilot) + +# Copilot: a stream DSL + +The visualizer, which draws Copilot specifications using different graphical +formats. + +Copilot is a runtime verification framework written in Haskell. It allows the +user to write programs in a simple but powerful way using a stream-based +approach. + +Programs can be interpreted for testing (with the library copilot-interpreter), +or translated C99 code to be incorporated in a project, or as a standalone +application. The C99 backend ensures us that the output is constant in memory +and time, making it suitable for systems with hard realtime requirements. + +## Installation + +Copilot-visualizer can be found on +[Hackage](https://hackage.haskell.org/package/copilot-interpreter). It is +typically only installed as part of the complete Copilot distribution. For +installation instructions, please refer to the [Copilot +website](https://copilot-language.github.io). + +## Further information + +For further information, install instructions and documentation, please visit +the Copilot website: +[https://copilot-language.github.io](https://copilot-language.github.io) + +## License + +Copilot is distributed under the BSD-3-Clause license, which can be found +[here](https://raw.githubusercontent.com/Copilot-Language/copilot/master/copilot-interpreter/LICENSE). diff --git a/copilot-visualizer/Setup.hs b/copilot-visualizer/Setup.hs new file mode 100644 index 000000000..9a994af67 --- /dev/null +++ b/copilot-visualizer/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal new file mode 100644 index 000000000..e57efd780 --- /dev/null +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -0,0 +1,130 @@ +cabal-version: >= 1.10 +name: copilot-visualizer +version: 4.4 +synopsis: Visualizer for Copilot. +description: + Visualizer for Copilot. + . + Copilot is a stream (i.e., infinite lists) domain-specific language (DSL) in + Haskell that compiles into embedded C. Copilot contains an interpreter, + multiple back-end compilers, and other verification tools. + . + A tutorial, examples, and other information are available at + . + +author: Ivan Perez, Frank Dedden, Ryan Scott, Kyle Beechly +license: BSD3 +license-file: LICENSE +maintainer: Ivan Perez +homepage: https://copilot-language.github.io +bug-reports: https://github.com/Copilot-Language/copilot/issues +stability: Experimental +category: Language, Embedded +build-type: Simple +extra-source-files: README.md + CHANGELOG + examples/gui/Main.hs + examples/gui/main.html + examples/gui/copilot.css + examples/gui/copilot.js + examples/tikz/Heater.hs + +data-files: data/tikz/tikz.tex + data/static_html/index.html + +x-curation: uncurated + +source-repository head + type: git + location: https://github.com/Copilot-Language/copilot.git + subdir: copilot-visualizer + +flag examples + description: Enable examples + default: False + manual: True + +library + + default-language: + Haskell2010 + + hs-source-dirs: + src + + ghc-options: + -Wall + + build-depends: + base >= 4.9 && < 5, + aeson >= 2.0.0.0 && < 2.3, + filepath >= 1.4.2 && < 1.6, + hint >= 0.9.0 && < 1.10, + pretty >= 1.0 && < 1.2, + ogma-extra >= 1.6.0 && < 1.7, + text >= 1.2.3.1 && < 2.2, + websockets >= 0.12.7 && < 0.14, + + copilot >= 4.4 && < 4.5, + copilot-core >= 4.4 && < 4.5, + copilot-interpreter >= 4.4 && < 4.5, + copilot-language >= 4.4 && < 4.5 + + exposed-modules: + + Copilot.Visualize.Live + Copilot.Visualize.Static + + other-modules: + + Copilot.Visualize.Dynamic + Copilot.Visualize.TypedTrace + Copilot.Visualize.UntypedTrace + Paths_copilot_visualizer + +executable gui-example + main-is: + Main.hs + + default-language: + Haskell2010 + + hs-source-dirs: + examples/gui + + ghc-options: + -Wall + + build-depends: + base >= 4.9 && < 5, + + copilot-visualizer + + if flag(examples) + buildable: True + else + buildable: False + +executable tikz-example + main-is: + Heater.hs + + default-language: + Haskell2010 + + hs-source-dirs: + examples/tikz + + ghc-options: + -Wall + + build-depends: + base >= 4.9 && < 5, + + copilot >= 4.4 && < 4.5, + copilot-visualizer + + if flag(examples) + buildable: True + else + buildable: False diff --git a/copilot-visualizer/data/static_html/index.html b/copilot-visualizer/data/static_html/index.html new file mode 100644 index 000000000..2160ec87a --- /dev/null +++ b/copilot-visualizer/data/static_html/index.html @@ -0,0 +1,255 @@ + + + + + + Copilot Visualizer + + + + + + + +
+ +
+ + + + diff --git a/copilot-visualizer/data/tikz/tikz.tex b/copilot-visualizer/data/tikz/tikz.tex new file mode 100644 index 000000000..b05f2c186 --- /dev/null +++ b/copilot-visualizer/data/tikz/tikz.tex @@ -0,0 +1,37 @@ +{{=<% %>=}} +\documentclass{standalone} +\usepackage{tikz} +\usepackage{tikz-timing} +\usepackage{xcolor} +\definecolor{false}{HTML}{ECD9ED} +\definecolor{true}{HTML}{D9ECED} +\begin{document} +\newcommand{\true}{T} +\newcommand{\false}{F} +\tikzset{ +every picture/.style={ + execute at end picture={ + \path (current bounding box.south west) +(-1,-1) (current bounding box.north east) +(1,1); + } +} +} + +\tikzset{ +timing/.style={x=5ex,y=2ex}, +timing/rowdist=4ex, +timing/dslope=0.1, +x=5ex, +timing/coldist=1ex, +timing/name/.style={font=\sffamily\scriptsize}, +timing/d/text/.style={font=\sffamily\tiny}, +} +\begin{tikztimingtable} +<%#adTraceElems%> +<%teName%> & g<%#teValues%><%#tvIsEmpty%>S<%/tvIsEmpty%><%^tvIsEmpty%><%#teIsBoolean%>[fill=<%tvValue%>]D{\<%tvValue%>}<%/teIsBoolean%><%^teIsBoolean%>D{<%tvValue%>}<%/teIsBoolean%><%/tvIsEmpty%><%/teValues%>\\ +<%/adTraceElems%> +\extracode +\begin{background}[shift={(0.05,0)},help lines] +\vertlines[help lines,opacity=0.3]{-0.3ex,...,<%adLastSample%>} +\end{background} +\end{tikztimingtable} +\end{document} diff --git a/copilot-visualizer/examples/gui/Main.hs b/copilot-visualizer/examples/gui/Main.hs new file mode 100644 index 000000000..8b93b7cbd --- /dev/null +++ b/copilot-visualizer/examples/gui/Main.hs @@ -0,0 +1,20 @@ +import Copilot.Visualize.Live + +main = visualize spec + +spec :: String +spec = unlines + [ "let temperature :: Stream Word8" + , " temperature = extern \"temperature\" (Just [0, 15, 20, 25, 30])" + , "" + , " ctemp :: Stream Float" + , " ctemp = (unsafeCast temperature) * (150.0 / 255.0) - 50.0" + , "" + , " trueFalse :: Stream Bool" + , " trueFalse = [True] ++ not trueFalse" + , "" + , "in do trigger \"heaton\" (temperature < 18) [arg ctemp, arg (constI16 1), arg trueFalse]" + , " trigger \"heatoff\" (temperature > 21) [arg (constI16 1), arg ctemp]" + , " observer \"temperature\" temperature" + , " observer \"temperature2\" (temperature + 1)" + ] diff --git a/copilot-visualizer/examples/gui/copilot.css b/copilot-visualizer/examples/gui/copilot.css new file mode 100644 index 000000000..82a1a0af6 --- /dev/null +++ b/copilot-visualizer/examples/gui/copilot.css @@ -0,0 +1,50 @@ +/* Timeline */ +/* Timeline decoration: Grid */ +.axis path, +.axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} +.grid-line { + stroke: #ccc; + stroke-width: 1; + stroke-dasharray: 3,3; +} + +/* Timeline decoration: Labels */ +.label { + font-size: 12px; +} +.time-label { + font-family: sans-serif; + font-size: 12px; + fill: black; +} +.value-text { + font-size: 10px; + text-anchor: middle; + dominant-baseline: middle; +} + +/* Timeline decoration: Values */ +.hexagon { + stroke: black; + stroke-width: 1px; + color: green; +} +.bool-background { + opacity: 0.3; +} + +/* Toolbar buttons */ +button { + width: 100px !important; + height: 48px !important; + border-radius: 24px !important; + background-color: #f1316b !important; + color: #fff !important; + margin-top: 45px !important; + border:none !important; + font-size: 12px !important; +} diff --git a/copilot-visualizer/examples/gui/copilot.js b/copilot-visualizer/examples/gui/copilot.js new file mode 100644 index 000000000..943e3c704 --- /dev/null +++ b/copilot-visualizer/examples/gui/copilot.js @@ -0,0 +1,489 @@ +/* Constants */ + +/* Timeline dimensions */ +const margin = {top: 20, right: 20, bottom: 30, left: 90}; +const width = 960 - margin.left - margin.right; +const height = 200 - margin.top - margin.bottom; +const rowHeight = 40; + +/** + * Position of the stream labels wrt. to the left side of the timeline. + */ +const labelPosX = -90; + +/* Timeline colors */ + +/* Default colors for true and false (just dim green and red). */ +const booleanColorDefaultTrue = "#90EE90"; +const booleanColorDefaultFalse = "#FFB6C1"; + +/* + * Alternative colors for true and false, making colored backgrounds more + * easily distinguishable in conditions with reduced color. Specifically: + * + * - Increase blue content in the 'false' color to increase distinction from + * the 'true' color in protanopic (no red) and deuteranopic (no green). + * + * - Decrease red content in the 'false' color to increase distinction from the + * 'true' color in achromatopsic (no color) conditions. + * + * This does not meaningfully address visibility considerations in reduced + * contrast or blurry conditions. + */ +const booleanColorHighContrastTrue = "#90EE90"; +const booleanColorHighContrastFalse = "#E1B6ED"; + +/* Timeline state */ +let svg; +let data = { adLastSample: 0, adTraceElems: [] }; +let xScale; +let numRows = 0; +let numSteps = 2; +let booleanColorTrue = booleanColorDefaultTrue; +let booleanColorFalse = booleanColorDefaultFalse; + +/** + * Process numeric data, adding duration and y-positions to each trace element. + */ +function processNumericData(data) +{ + // Calculate bounds of stream. + const maxValue = Math.max(...data.map(d => d.tvValue)); + const minValue = Math.min(...data.map(d => d.tvValue)); + + // Vertical scale of the stream. + const yScale = d3.scaleLinear() + .domain([minValue, maxValue]) + .range([rowHeight * 0.8, rowHeight * 0.2]); + + // Return the data augmented with duration for each sample, and the vertical + // scale. + return data.map((d, i) => ({ + ...d, + duration: + i < data.length - 1 ? data[i + 1].time - d.time : numSteps + 1 - d.time, + y: yScale(d.tvValue) + })); +} + +/** + * Create timeline labels. + */ +function createLabels() +{ + document.querySelectorAll(".label").forEach(function(lst) { + lst.remove(); + }); + + // Add row labels. + const labels = data.adTraceElems.map((x) => x.teName); + svg.selectAll(".label") + .data(labels) + .enter() + .append("text") + .attr("class", "label") + .attr("x", labelPosX) + .attr("y", (d, i) => i * rowHeight + rowHeight/2) + .text(d => d); +} + +/** + * Draw numeric row with stretched hexagons. + */ +function drawNumericRow(data, rowIndex) +{ + const rowG = svg.append("g") + .attr("class", "value_list") + .attr("transform", `translate(0,${rowIndex * rowHeight})`); + + const hexHeight = rowHeight * 0.8; + + time = 0; + + // Create hexagon groups. + const hexGroups = rowG.selectAll("g") + .data(data) + .enter() + .append("g") + .attr("transform", d => `translate(${xScale(time++)},${rowHeight * 0.1})`); + + // Add hexagons. + hexGroups.append("path") + .attr("class", "hexagon") + .attr("d", d => hexagonPath(xScale(1) - xScale(0), hexHeight)) + .attr("fill", "#f7fbff"); + + // Add value text. + hexGroups.append("text") + .attr("class", "value-text") + .attr("x", d => (xScale(1) - xScale(0)) / 2) + .attr("y", hexHeight / 2) + .text(d => d.tvValue); +} + +/** + * Draw boolean rows with colored backgrounds. + */ +function drawBooleanRow(data, rowIndex) +{ + const rowG = svg.append("g") + .attr("class", "value_list") + .attr("transform", `translate(0,${rowIndex * rowHeight})`); + + time = 0; + + // Draw colored backgrounds. + rowG.selectAll("rect") + .data(data) + .enter() + .append("rect") + .attr("class", "bool-background") + .attr("x", d => xScale(time++)) + .attr("y", 0) + .attr("width", d => xScale(1) - xScale(0)) + .attr("height", rowHeight) + .attr("fill", d => d.tvValue == "true" ? booleanColorTrue : booleanColorFalse); +} + +/** + * Re-draw complete timeline. + */ +function redraw() +{ + document.querySelectorAll(".value_list").forEach(function(lst) { + lst.remove(); + }); + + // Draw all rows. + i = 0; + data.adTraceElems.forEach(function(traceElem) { + if (traceElem.teIsBoolean) + { + drawBooleanRow(traceElem.teValues, i); + } + else + { + drawNumericRow(processNumericData(traceElem.teValues), i); + } + i++; + }); +} + +/** + * Create SVG for the timeline. + */ +function createSVG() +{ + // Clear all existing elements. + if (typeof svg !== 'undefined' ) { svg.selectAll("*").remove(); } + + // Calculate number of rows and columns (steps) from available data. + numRows = data.adTraceElems.length; + numSteps = data.adLastSample; + + // Create SVG. + svg = d3.select("#timeline") + .attr("width", width + margin.left + margin.right) + .attr("height", (rowHeight * numRows) + margin.top + margin.bottom) + .append("g") + .attr("transform", `translate(${margin.left},${margin.top})`); + + // Create scales. + xScale = d3.scaleLinear() + .domain([0, numSteps + 1]) + .range([0, width]); + + // Create x-axis with ticks at integer positions. + const xAxis = d3.axisBottom(xScale) + .tickValues(Array.from({length: 11}, (_, i) => i)) + .tickFormat(''); + + createLabels(); + redraw(); + + // Add the axis. + const axisGroup = svg.append("g") + .attr("class", "axis") + .attr("transform", `translate(0,${rowHeight * numRows})`) + .call(xAxis); + + // After creating the axis and labels, add vertical grid lines. + // + // Use light gray as color, a dotted line, start from the top (negative + // because we are going up from the axis), and end at the axis. + const gridLines = axisGroup.selectAll(".grid-line") + .data(Array.from({length: 11}, (_, i) => i)) + .enter() + .append("line") + .attr("class", "grid-line") + .attr("x1", d => xScale(d)) + .attr("x2", d => xScale(d)) + .attr("y1", -rowHeight * (numRows + 1)) + .attr("y2", 0) + .style("stroke", "#ccc") + .style("stroke-width", "1") + .style("stroke-dasharray", "3,3"); + + // Add offset labels. + const timeLabels = axisGroup.selectAll(".time-label") + .data(Array.from({length: numRows + 1}, (_, i) => i)) + .enter() + .append("text") + .attr("class", "time-label") + .attr("x", d => xScale(d + 0.5)) + .attr("y", 25) + .attr("text-anchor", "middle") + .text(d => d + ""); + + // Detect clicks on timeline. + svg.on("click", function(event) { + const mouseX = d3.pointer(event)[0]; + const time = Math.floor(xScale.invert(mouseX).toFixed(2)); + const row = Math.floor((event.offsetY - margin.top) / rowHeight); + const label = data.adTraceElems[row].teName; + + console.log("Label:", label , "Time:", time); + ws.send('(Up ' + time + ', "' + label + '")'); + }); + + // Detect mouse wheel on timeline. + svg.on("wheel", function(event) { + // Prevent default scroll behavior. + event.preventDefault(); + + const mouseX = d3.pointer(event)[0]; + const time = Math.floor(xScale.invert(mouseX).toFixed(2)); + const row = Math.floor((event.offsetY - margin.top) / rowHeight); + const label = data.adTraceElems[row].teName; + const dir = event.deltaY < 0 ? 'Up' : 'Down'; + + ws.send('(' + dir + ' ' + time + ', "' + label + '")'); + + console.log ( "Label:", label, "Time:", time, "Direction:", dir); + }); +} + +/** + * Add a step to the simulation. + */ +function stepUp() +{ + ws.send('(StepUp, "")'); +} + +/** + * Reduce the number of steps of the simulation. + */ +function stepDown() +{ + ws.send('(StepDown, "")'); +} + +/** + * Open a dialog requesting information to add a new stream to the simulation. + */ +function addStream() +{ + const popup = document.getElementById("popup"); + popup.classList.remove("hidden"); +} + +/** + * Cancel dialog requesting information to add a new stream to the simulation. + */ +function closePopup() +{ + const popup = document.getElementById("popup"); + popup.classList.add("hidden"); +} + +/** + * Add a new stream to the simulation. The definition provided must be correct + * in the context of the current simulation, and the name given to the new + * stream must be valid in Haskell, unique in the current context, and not + * clash with any existing names. + */ +function addStreamSubmit() +{ + const popup = document.getElementById("popup"); + const inputName = document.getElementById("inputName").value; + const inputExpr = document.getElementById("inputExpr").value; + ws.send('(AddStream "' + inputName + '" "(' + inputExpr + ')", "")'); + popup.classList.add("hidden"); +} + +/** + * Toggle the color scheme of the timeline. + */ +function toggleColors() +{ + const highContrastBtn = document.getElementById('high_contrast'); + if (highContrastBtn.checked) { + booleanColorTrue = booleanColorHighContrastTrue; + booleanColorFalse = booleanColorHighContrastFalse; + } else { + booleanColorTrue = booleanColorDefaultTrue; + booleanColorFalse = booleanColorDefaultFalse; + } + + createSVG(); +} + +/* Attach event handlers to UI elements */ + +// Somehow this does not work if we match the svg element directly, so we match +// the parent div. +document.getElementById("copyPNG").addEventListener("click", () => copyPNG("#timeline-parent")); + +document.getElementById("copySVG").addEventListener("click", () => copySVG("#timeline")); + +// Somehow this does not work if we match the svg element directly, so we match +// the parent div. +document.getElementById("downloadPNG").addEventListener("click", () => downloadPNG("#timeline-parent", "timeline")); + +document.getElementById("downloadSVG").addEventListener("click", () => downloadSVG("#timeline", "timeline")); + +document.getElementById("stepUp").addEventListener("click", stepUp); +document.getElementById("stepDown").addEventListener("click", stepDown); +document.getElementById("addStream").addEventListener("click", addStream); +document.getElementById("closePopup").addEventListener("click", closePopup); +document.getElementById("submitPopup").addEventListener("click", addStreamSubmit); + +// Adjust colors using high-contrast when applicable. +document.getElementById('high_contrast').addEventListener('change', toggleColors); + +createSVG(); + +// Communicate with the backend instance of Copilot via a websocket. +let ws = new WebSocket('ws://localhost:9160'); + +ws.onmessage = function(event) { + data = JSON.parse(event.data); + createSVG(); +}; + +ws.onclose = function() { + console.log('Disconnected from server'); +}; + +ws.onerror = function(error) { + console.log('Error: ' + error.message); +}; + +/* Auxiliary functions */ + +/** + * Create an hexagon path (used for cells or samples). + */ +function hexagonPath(width, height) +{ + return ` + M ${width-10},0 + L ${width},${height/2} + L ${width-10},${height} + L ${10},${height} + L ${0},${height/2} + L ${10},0 + Z + `; +} + +/** + * Download a file containing a given text. + */ +function download(filename, text) +{ + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); +} + +/** + * Copy element as SVG to clipboard as SVG. + * + * @param {svg} queryString - Query string referring to an SVG element in the + * current document. + */ +function copySVG(queryString) +{ + const mySVG = document.querySelector(queryString); + const data = new XMLSerializer().serializeToString(mySVG); + + navigator.clipboard.writeText(data).then( + () => { + console.log("SVG copied to clipboard"); + }, + (err) => { + console.log("Failed to copy SVG to clipboard"); + } + ); +} + +/** + * Copy timeline to clipboard as PNG. + * + * @param {string} queryString - Query string referring to element in the + * current document. + */ +function copyPNG(queryString) +{ + const elem = document.querySelector(queryString); + html2canvas(elem).then(function(canvas) { + canvas.toBlob(function(blob) { + navigator.clipboard + .write([ + new ClipboardItem( + Object.defineProperty({}, blob.type, { + value: blob, + enumerable: true + }) + ) + ]).then(function() { + console.log("Copied to clipboard"); + }); + }); + }); +} + +/** + * Download screenshot of an element as a PNG file. + * + * @param {string} queryString - Query string referring to element in the + * current document. + * + * @param {string} defaultFilename - Default file name used in the save file + * dialog. + */ +function downloadPNG(queryString, defaultFilename) +{ + const mySVG = document.querySelector(queryString); + html2canvas(mySVG).then(function(canvas) { + var a = document.createElement('a'); + a.href = canvas.toDataURL(); + a.download = defaultFilename + ".png"; + a.click(); + }); +} + +/** + * Download screenshot of timeline as SVG file. + * + * @param {string} queryString - Query string referring to element in the + * current document. + * + * @param {string} defaultFilename - Default file name used in the save file + * dialog. + */ +function downloadSVG(queryString, defaultFilename) +{ + const mySVG = document.querySelector(queryString); + const data = new XMLSerializer().serializeToString(mySVG); + download(defaultFilename + ".svg", data); +} diff --git a/copilot-visualizer/examples/gui/main.html b/copilot-visualizer/examples/gui/main.html new file mode 100644 index 000000000..a2003d72a --- /dev/null +++ b/copilot-visualizer/examples/gui/main.html @@ -0,0 +1,67 @@ + + + Copilot Visualizer + + + + + + + + + + + + + + +
+ +
+ + +
+ + + + + + + +
+ + +
+ +
+ + + + + + + + diff --git a/copilot-visualizer/examples/tikz/Heater.hs b/copilot-visualizer/examples/tikz/Heater.hs new file mode 100644 index 000000000..e36cde926 --- /dev/null +++ b/copilot-visualizer/examples/tikz/Heater.hs @@ -0,0 +1,37 @@ +-- Copyright 2025 NASA +-- Copyright 2019 National Institute of Aerospace / Galois, Inc. + +-- This is a simple example with basic usage. It implements a simple home +-- heating system: It heats when temp gets too low, and stops when it is high +-- enough. It read temperature as a byte (range -50C to 100C) and translates +-- this to Celsius. + +module Main where + +import Language.Copilot +import Copilot.Visualize.Static + +import Prelude hiding ((>), (<)) + +-- External temperature as a byte, range of -50C to 100C +temp :: Stream Word8 +temp = extern "temperature" (Just [ 10, 15, 18, 19, 25, 5, 45 ]) + +-- Calculate temperature in Celsius. +-- We need to cast the Word8 to a Float. Note that it is an unsafeCast, as there +-- is no direct relation between Word8 and Float. +ctemp :: Stream Float +ctemp = (unsafeCast temp) * (150.0 / 255.0) - 50.0 + +spec = do + -- Triggers that fire when the ctemp is too low or too high, + -- pass the current ctemp as an argument. + trigger "heaton" (ctemp < 18.0) [arg ctemp] + trigger "heatoff" (ctemp > 21.0) [arg ctemp] + +-- Compile the spec and produce a LaTeX file in the current directory with a +-- Tikz drawing of the spec. +main :: IO () +main = do + spec' <- reify spec + visualize 3 spec' "tikz" "." diff --git a/copilot-visualizer/src/Copilot/Visualize/Dynamic.hs b/copilot-visualizer/src/Copilot/Visualize/Dynamic.hs new file mode 100644 index 000000000..26c535a93 --- /dev/null +++ b/copilot-visualizer/src/Copilot/Visualize/Dynamic.hs @@ -0,0 +1,253 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +-- | +-- Copyright : (c) NASA, 2024-2025 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Allow loading Copilot specifications from strings and modifying them live. +-- +-- An example of a spec that can be passed as argument to the live simulator +-- follows: +-- +-- @ +-- spec :: String +-- spec = unlines +-- [ "let temperature :: Stream Word8" +-- , " temperature = extern \"temperature\" (Just [0, 15, 20, 25, 30])" +-- , "" +-- , " ctemp :: Stream Float" +-- , " ctemp = (unsafeCast temperature) * (150.0 / 255.0) - 50.0" +-- , "" +-- , " trueFalse :: Stream Bool" +-- , " trueFalse = [True] ++ not trueFalse" +-- , "" +-- , "in do trigger \"heaton\" (temperature < 18) [arg ctemp, arg (constI16 1), arg trueFalse]" +-- , " trigger \"heatoff\" (temperature > 21) [arg (constI16 1), arg ctemp]" +-- , " observer \"temperature\" temperature" +-- , " observer \"temperature2\" (temperature + 1)" +-- ] +-- @ +-- +-- The imports are predefined. +module Copilot.Visualize.Dynamic + ( simInit + , simStep + , SimulationSettings(..) + , mkDefaultSimulationSettings + , SimData(..) + ) + where + +-- External imports +import qualified Language.Haskell.Interpreter as HI +import Prelude hiding (div, not, (++), (<), (>)) +import qualified Prelude + +-- External imports: Copilot +import qualified Copilot.Core as Core +import Copilot.Language hiding (interpret, typeOf) +import Language.Copilot hiding (interpret, typeOf) + +-- Internal imports +import Copilot.Visualize.TypedTrace + +-- | Simulation data. +data SimData = SimData + { simSteps :: Int -- ^ Number of steps to simulate. + , simSpec :: Core.Spec -- ^ Copilot spec to simulate. + , simString :: String -- ^ Textual representation of Copilot spec. + } + +-- | Initialize a simulation. +simInit :: SimulationSettings -> String -> IO SimData +simInit simulationSettings spec = do + -- Load the spec, and save it so that it doesn't need to be reloaded if it + -- doesn't change. + let numSteps = simulationSettingsInitialSteps simulationSettings + spec' <- readSpec simulationSettings spec + let simData = SimData numSteps spec' spec + return simData + +-- | Update the spec based on the input command received. +simStep :: SimulationSettings + -> SimData + -> (Command, String) + -> IO SimData +simStep _ simData (StepUp, _) = + pure simData { simSteps = simSteps simData + 1 } + +simStep _ simData (StepDown, _) = + pure simData { simSteps = simSteps simData + 1 } + +simStep simulationSettings simData (AddStream name expr, _) = do + let specN = concat [ simString simData, "\n", " ", completeExpr] + + completeExpr = concat [ "observer ", show name, " (", expr, ")" ] + + let trace = extractTrace $ simSpec simData + spec2 <- readSpec simulationSettings specN + let spec3 = updateWithTrace trace spec2 + + pure simData { simSpec = spec3, simString = specN } + +simStep simulationSettings simData (command, name) = do + spec' <- apply simulationSettings (simSpec simData) name command + pure simData { simSpec = spec' } + +-- | Settings used for a simulation. +data SimulationSettings = SimulationSettings + { simulationSettingsInitialSteps :: Int + , simulationSettingsImports :: [(String, Maybe String)] + } + +-- | Default settings that simulates 3 steps and provides default imports of +-- the main Copilot modules. +mkDefaultSimulationSettings :: SimulationSettings +mkDefaultSimulationSettings = + SimulationSettings + 3 + [ ("Control.Monad.Writer", Nothing) + , ("Copilot.Language", Nothing) + , ("Copilot.Language.Spec", Nothing) + , ("Data.Functor.Identity", Nothing) + , ("Language.Copilot", Nothing) + , ("Prelude", Just "P") + ] + +-- * Commands + +-- * Load specs from strings + +-- | Read a specification from a string and reify it. +readSpec :: SimulationSettings -> String -> IO Core.Spec +readSpec settings spec = do + r <- HI.runInterpreter (loadSpec settings spec) + case r of + Left err -> do putStrLn $ "Error: " Prelude.++ show err + error $ show err + Right s -> return s + +-- | Load a spec from a string, adding standard imports. +loadSpec :: SimulationSettings -> String -> HI.Interpreter Core.Spec +loadSpec settings spec = do + HI.setImportsQ (simulationSettingsImports settings) + spec' <- HI.interpret spec (HI.as :: Spec) + HI.liftIO $ reify spec' + +-- | Commands to apply to a running simulation. +data Command = StepUp + | StepDown + | Up Int + | Down Int + | AddStream String String + | Noop + deriving (Eq, Read, Show) + +-- | Apply a command to a simulation. +apply :: SimulationSettings -> Core.Spec -> String -> Command -> IO Core.Spec +apply settings spec _name (AddStream sName sExpr) = do + spec' <- addStream settings sName sExpr + -- TODO: I need to bring the streams from the other spec too, otherwise the + -- streams to include may refer to streams by ID that are in a different + -- scope. + let observers' = Core.specObservers spec' + observers = Core.specObservers spec + return $ spec { Core.specObservers = observers Prelude.++ observers' } + +apply _settings spec name command = pure $ spec + { Core.specStreams = + map (updateStream name command) (Core.specStreams spec) + , Core.specObservers = + map (updateObserver name command) (Core.specObservers spec) + , Core.specTriggers = + map (updateTrigger name command) (Core.specTriggers spec) + } + +-- | Apply a command to an observer. +updateObserver :: String -> Command -> Core.Observer -> Core.Observer +updateObserver name command (Core.Observer i e ty) = + Core.Observer i (updateExpr name command e) ty + +-- | Apply a command to a trigger. +updateTrigger :: String -> Command -> Core.Trigger -> Core.Trigger +updateTrigger name command (Core.Trigger i e es) = Core.Trigger i e' es' + where + e' = updateExpr name command e + es' = map (updateUExpr name command) es + +-- | Apply a command to a core expression. +updateExpr :: String -> Command -> Core.Expr a -> Core.Expr a +updateExpr name command expr = case expr of + Core.ExternVar ty nameE vs + | nameE Prelude.== name + -> Core.ExternVar ty nameE (updateValues vs ty command) + + | otherwise + -> expr + + Core.Op1 op expr1 -> + Core.Op1 op (updateExpr name command expr1) + + Core.Op2 op expr1 expr2 -> + Core.Op2 op (updateExpr name command expr1) (updateExpr name command expr2) + + Core.Op3 op expr1 expr2 expr3 -> + Core.Op3 + op + (updateExpr name command expr1) + (updateExpr name command expr2) + (updateExpr name command expr3) + + _ -> expr + +-- | Apply a command to an untyped expression that carries the information +-- about the type of the expression as a value (existential). +updateUExpr :: String -> Command -> Core.UExpr -> Core.UExpr +updateUExpr name cmd (Core.UExpr ty expr) = + Core.UExpr ty (updateExpr name cmd expr) + +-- | Apply a command to a stream. +updateStream :: String -> Command -> Core.Stream -> Core.Stream +updateStream name command (Core.Stream i buf expr ty) = + Core.Stream i buf (updateExpr name command expr) ty + +-- | Apply a command to a series of typed values, if any. +updateValues :: Maybe [a] -> Type a -> Command -> Maybe [a] +updateValues vsM ty command = + fmap (fmap (updateValue command ty) . zip [0..]) vsM + +-- | Apply a command to a typed value. +updateValue :: Command -> Type a -> (Int, a) -> a +updateValue (Up n) Core.Bool (ix, a) = + if n Prelude.== ix then Prelude.not a else a +updateValue (Down n) Core.Bool (ix, a) = + if n Prelude.== ix then Prelude.not a else a +updateValue (Up n) Core.Word8 (ix, a) = + if n Prelude.== ix then a + 1 else a +updateValue (Down n) Core.Word8 (ix, a) = + if n Prelude.== ix then a - 1 else a +updateValue _ _ (_, a) = a + +-- * Sample spec + +-- | Produce a specification from the expression for a stream. +addStream :: SimulationSettings -> String -> String -> IO Core.Spec +addStream settings name expr = do + r <- HI.runInterpreter (addStream' settings name expr) + case r of + Left err -> do putStrLn $ "Error: " Prelude.++ show err + error $ show err + Right spec -> return spec + +-- | Produce a specification from the expression for a stream, in an +-- interpreter context. +-- +-- Observe that Interpreter () is an alias for InterpreterT IO () +addStream' :: SimulationSettings -> String -> String -> HI.Interpreter Core.Spec +addStream' settings name expr = do + HI.setImportsQ (simulationSettingsImports settings) + let completeExpr = concat [ "observer ", show name, " (", expr, ")" ] + + spec <- HI.interpret completeExpr (HI.as :: Spec) + HI.liftIO $ reify spec diff --git a/copilot-visualizer/src/Copilot/Visualize/Live.hs b/copilot-visualizer/src/Copilot/Visualize/Live.hs new file mode 100644 index 000000000..973cd2feb --- /dev/null +++ b/copilot-visualizer/src/Copilot/Visualize/Live.hs @@ -0,0 +1,162 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +-- | +-- Copyright : (c) NASA, 2024-2025 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Run a Copilot simulation live and allow interacting with it using a +-- websocket. +-- +-- This visualizer enables adding new streams to a visualization. To do so, the +-- visualizer needs access to the original spec, and needs to be able to +-- interpret new expressions in the same context as the prior expressions. +-- +-- An example of a spec that can be passed as argument to the visualizer +-- follows: +-- +-- @ +-- spec :: String +-- spec = unlines +-- [ "let temperature :: Stream Word8" +-- , " temperature = extern \"temperature\" (Just [0, 15, 20, 25, 30])" +-- , "" +-- , " ctemp :: Stream Float" +-- , " ctemp = (unsafeCast temperature) * (150.0 / 255.0) - 50.0" +-- , "" +-- , " trueFalse :: Stream Bool" +-- , " trueFalse = [True] ++ not trueFalse" +-- , "" +-- , "in do trigger \"heaton\" (temperature < 18) [arg ctemp, arg (constI16 1), arg trueFalse]" +-- , " trigger \"heatoff\" (temperature > 21) [arg (constI16 1), arg ctemp]" +-- , " observer \"temperature\" temperature" +-- , " observer \"temperature2\" (temperature + 1)" +-- ] +-- @ +-- +-- The imports are predefined. +module Copilot.Visualize.Live + ( visualize + , visualizeWith + , VisualSettings(..) + , mkDefaultVisualSettings + , SimulationSettings(..) + , mkDefaultSimulationSettings + ) + where + +-- External imports +import Control.Exception (SomeException (..), handle) +import Data.Aeson (ToJSON (..), encode) +import qualified Data.Text as T +import qualified Network.WebSockets as WS +import Prelude hiding (div, not, (++), (<), (>)) +import qualified Prelude +import Text.Read (readMaybe) + +-- External imports: Copilot +import qualified Copilot.Core as Core +import Copilot.Interpret.Eval (ShowType (Haskell), eval) +import Copilot.Language hiding (interpret, typeOf) +import Copilot.Visualize.UntypedTrace (AppData, makeTraceEval) + +-- Internal imports +import Copilot.Visualize.Dynamic + +-- | Start a simulation for an input spec, listening for commands and +-- communicating the status via a websocket. +visualize :: String -> IO () +visualize = visualizeWith mkDefaultVisualSettings + +-- | Start a simulation for an input spec, listening for commands and +-- communicating the status via a websocket. +visualizeWith :: VisualSettings -> String -> IO () +visualizeWith settings spec = do + putStrLn $ concat + [ "WebSocket server starting on port " + , show (visualSettingsPort settings) + , "..." + ] + WS.runServer + (visualSettingsHost settings) + (visualSettingsPort settings) + (app settings spec) + +-- | Settings used to customize the simulation and interaction. +data VisualSettings = VisualSettings + { visualSettingsHost :: String + -- ^ Host interface to listen to. Use + -- "127.0.0.1" to listen at localhost. + + , visualSettingsPort :: Int + -- ^ Port to listen to. + + , visualSettingsSimulation :: SimulationSettings + -- ^ Settings for the simulation. + } + +-- | Default settings that simulates 3 steps and listens on localhost at port +-- 9160. +mkDefaultVisualSettings :: VisualSettings +mkDefaultVisualSettings = VisualSettings + { visualSettingsHost = "127.0.0.1" + , visualSettingsPort = 9160 + , visualSettingsSimulation = mkDefaultSimulationSettings + } + +-- * Server + +-- | Server application using web sockets. +app :: VisualSettings -> String -> WS.ServerApp +app settings spec pending = do + conn <- WS.acceptRequest pending + WS.withPingThread conn 30 (return ()) $ appInit settings spec conn + +-- | Initialize the backend. +appInit :: VisualSettings -> String -> WS.Connection -> IO () +appInit settings spec conn = handle appException $ do + -- Initialize the simulation. + simData <- simInit (visualSettingsSimulation settings) spec + + -- Communicate the current values of the trace, in JSON, via the web + -- socket. + let appData = mkAppData (simSteps simData) (simSpec simData) + let samples = encode $ toJSON appData + WS.sendTextData conn samples + + -- Start the application loop. + appMainLoop settings conn simData + + where + + appException :: SomeException -> IO () + appException e = do + putStrLn $ "Error:" Prelude.++ show e + error $ show e + +-- | Run the main application, repeatedly reading commands from a web socket +-- and returning results via the same web socket. +appMainLoop :: VisualSettings + -> WS.Connection + -> SimData + -> IO () +appMainLoop settings conn simData = do + -- Read a command from the web socket. + cmdM <- readMaybe . T.unpack <$> WS.receiveData conn + + -- Run a simulation step, if a command has been received. + let simulationSettings = visualSettingsSimulation settings + simData' <- maybe (pure simData) (simStep simulationSettings simData) cmdM + + -- Communicate the current values of the trace, in JSON, via the web socket. + let appData = mkAppData (simSteps simData') (simSpec simData') + samples = encode $ toJSON appData + WS.sendTextData conn samples + + appMainLoop settings conn simData' + +-- * Auxiliary functions + +-- | Obtain the trace data from a Copilot spec for a number of steps. +mkAppData :: Int -> Core.Spec -> AppData +mkAppData numSteps spec' = + makeTraceEval numSteps spec' (eval Haskell numSteps spec') diff --git a/copilot-visualizer/src/Copilot/Visualize/Static.hs b/copilot-visualizer/src/Copilot/Visualize/Static.hs new file mode 100644 index 000000000..bfe5f34a8 --- /dev/null +++ b/copilot-visualizer/src/Copilot/Visualize/Static.hs @@ -0,0 +1,36 @@ +-- | +-- Copyright : (c) NASA, 2024-2025 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Produce a visual representation of a Copilot specification. +module Copilot.Visualize.Static + ( visualize + ) + where + +-- External imports +import Data.Aeson (ToJSON (..)) +import System.Directory.Extra (copyTemplate) +import System.FilePath (()) + +-- External imports: Copilot +import Copilot.Core (Spec (..)) +import Copilot.Interpret.Eval (ShowType (Haskell), eval) + +-- Internal imports +import Copilot.Visualize.UntypedTrace (makeTraceEval) +import Paths_copilot_visualizer (getDataDir) + +-- | Produce a visual representation of a Copilot specification, for a given +-- number of steps, using a template. +visualize :: Int -- ^ Number of steps to interpret. + -> Spec -- ^ Specification to interpret. + -> String -- ^ Base used to expand the static file (i.e., @"tikz"@, + -- @"static_html"@). + -> FilePath + -> IO () +visualize k spec base target = do + dir <- getDataDir + let f = dir "data" base + let subs = toJSON $ makeTraceEval k spec $ eval Haskell k spec + copyTemplate f subs target diff --git a/copilot-visualizer/src/Copilot/Visualize/TypedTrace.hs b/copilot-visualizer/src/Copilot/Visualize/TypedTrace.hs new file mode 100644 index 000000000..901f42820 --- /dev/null +++ b/copilot-visualizer/src/Copilot/Visualize/TypedTrace.hs @@ -0,0 +1,158 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +-- | +-- Copyright : (c) NASA, 2024-2025 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Read and modify traces of specifications. +-- +-- In the context of this module, a trace refers only to the values of +-- externs, since the state of a spec can be reproduced if the values of +-- externs are known. +module Copilot.Visualize.TypedTrace + ( extractTrace + , updateWithTrace + , Trace(..) + , UValues(..) + ) + where + +-- External imports +import Data.List hiding ((++)) +import Data.Maybe (fromMaybe) +import qualified Data.Type.Equality as DE +import Data.Typeable (Typeable) +import Prelude hiding (div, not, (++), (<), (>)) + +-- External imports: Copilot +import qualified Copilot.Core as Core +import Language.Copilot hiding (interpret, typeOf) + +-- | Map stream names to their values. +newtype Trace = Trace + { traceMap :: [ (String, UValues) ] + } + +-- | Existentially typed list of values of a stream. +data UValues = forall a . Typeable a => UValues + { uvType :: Core.Type a + , uvValues :: [ a ] + } + +-- | Extract a trace from a core specification. +extractTrace :: Core.Spec -> Trace +extractTrace spec = Trace $ concat $ concat + [ fmap extractTraceStream (Core.specStreams spec) + , fmap extractTraceObserver (Core.specObservers spec) + , fmap extractTraceTrigger (Core.specTriggers spec) + ] + +-- | Extract the values of a core 'Stream'. +extractTraceStream :: Core.Stream -> [ (String, UValues) ] +extractTraceStream (Core.Stream _id _buf expr _ty) = + extractTraceExpr expr + +-- | Extract the values of a core 'Observer'. +extractTraceObserver :: Core.Observer -> [ (String, UValues) ] +extractTraceObserver (Core.Observer _name expr _ty) = + extractTraceExpr expr + +-- | Extract the values of a core 'Trigger'. +extractTraceTrigger :: Core.Trigger -> [ (String, UValues) ] +extractTraceTrigger (Core.Trigger _name expr args) = concat $ + extractTraceExpr expr + : fmap extractTraceUExpr args + +-- | Extract the values of a core 'Expr'. +extractTraceExpr :: Core.Expr a -> [ (String, UValues) ] +extractTraceExpr (Core.Local _ _ _ expr1 expr2) = concat + [ extractTraceExpr expr1 + , extractTraceExpr expr2 + ] +extractTraceExpr (Core.ExternVar ty name values) = + [ (name, UValues ty (fromMaybe [] values)) ] +extractTraceExpr (Core.Op1 _op expr) = + extractTraceExpr expr +extractTraceExpr (Core.Op2 _op expr1 expr2) = concat + [ extractTraceExpr expr1 + , extractTraceExpr expr2 + ] +extractTraceExpr (Core.Op3 _op expr1 expr2 expr3) = concat + [ extractTraceExpr expr1 + , extractTraceExpr expr2 + , extractTraceExpr expr3 + ] +extractTraceExpr (Core.Label _ty _lbl expr) = + extractTraceExpr expr +extractTraceExpr _ = [] + +-- | Extract the values of a core 'UExpr'. +extractTraceUExpr :: Core.UExpr -> [ (String, UValues) ] +extractTraceUExpr (Core.UExpr ty expr) = extractTraceExpr expr + +-- | Update externs in a spec. +updateWithTrace :: Trace -> Core.Spec -> Core.Spec +updateWithTrace trace spec = spec + { Core.specStreams = + fmap (updateWithTraceStream trace) (Core.specStreams spec) + , Core.specObservers = + fmap (updateWithTraceObserver trace) (Core.specObservers spec) + , Core.specTriggers = + fmap (updateWithTraceTrigger trace) (Core.specTriggers spec) + } + +-- | Update the values of a 'Stream' based on an input trace. +updateWithTraceStream :: Trace -> Core.Stream -> Core.Stream +updateWithTraceStream trace (Core.Stream ident buf expr ty) = + Core.Stream ident buf (updateWithTraceExpr trace expr) ty + +-- | Update the values of an 'Observer' based on an input trace. +updateWithTraceObserver :: Trace -> Core.Observer -> Core.Observer +updateWithTraceObserver trace (Core.Observer name expr ty) = + Core.Observer name (updateWithTraceExpr trace expr) ty + +-- | Update the values of a 'Trigger' based on an input trace. +updateWithTraceTrigger :: Trace -> Core.Trigger -> Core.Trigger +updateWithTraceTrigger trace (Core.Trigger name expr args) = + Core.Trigger + name + (updateWithTraceExpr trace expr) + (fmap (updateWithTraceUExpr trace) args) + +-- | Update the values of an 'Expr' based on an input trace. +updateWithTraceExpr :: Trace -> Core.Expr a -> Core.Expr a +updateWithTraceExpr trace (Core.Local ty1 ty2 name expr1 expr2) = + Core.Local + ty1 + ty2 + name + (updateWithTraceExpr trace expr1) + (updateWithTraceExpr trace expr2) +updateWithTraceExpr trace (Core.ExternVar ty name values) = + Core.ExternVar ty name values' + where + values' | Just (UValues ty2 vals) <- lookup name (traceMap trace) + , Just DE.Refl <- DE.testEquality ty ty2 + = Just vals + | otherwise + = values +updateWithTraceExpr trace (Core.Op1 op expr) = + Core.Op1 op (updateWithTraceExpr trace expr) +updateWithTraceExpr trace (Core.Op2 op expr1 expr2) = + Core.Op2 op + (updateWithTraceExpr trace expr1) + (updateWithTraceExpr trace expr2) +updateWithTraceExpr trace (Core.Op3 op expr1 expr2 expr3) = + Core.Op3 op + (updateWithTraceExpr trace expr1) + (updateWithTraceExpr trace expr2) + (updateWithTraceExpr trace expr3) +updateWithTraceExpr trace (Core.Label ty lbl expr) = + Core.Label ty lbl (updateWithTraceExpr trace expr) +updateWithTraceExpr trace x = x + +-- | Update the values of a 'UExpr' based on an input trace. +updateWithTraceUExpr :: Trace -> Core.UExpr -> Core.UExpr +updateWithTraceUExpr trace (Core.UExpr ty expr) = + Core.UExpr ty (updateWithTraceExpr trace expr) diff --git a/copilot-visualizer/src/Copilot/Visualize/UntypedTrace.hs b/copilot-visualizer/src/Copilot/Visualize/UntypedTrace.hs new file mode 100644 index 000000000..6bbc8365d --- /dev/null +++ b/copilot-visualizer/src/Copilot/Visualize/UntypedTrace.hs @@ -0,0 +1,176 @@ +{-# LANGUAGE DeriveGeneric #-} +-- | +-- Copyright : (c) NASA, 2024-2025 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Abstract representation of a trace as a series named streams with their +-- values, together with functions needed to convert a 'Spec' into a trace. +-- +-- This trace is flat (untyped). Information about the types is captured +-- using boolean flags. This kind of trace is most useful to render values +-- on in logicless templates, since they don't have the capability to examine +-- the Haskell types. +module Copilot.Visualize.UntypedTrace + ( AppData(..) + , Trace + , TraceElem(..) + , TraceValue(..) + , makeTraceEval + ) + where + +-- External imports +import Data.Aeson (ToJSON (..)) +import Data.Bifunctor (second) +import Data.List (find, transpose) +import Data.Maybe (isJust, isNothing) +import GHC.Generics (Generic) +import Text.Printf (printf) +import Text.Read (readMaybe) + +-- External imports: Copilot +import Copilot.Core (Spec (..), triggerArgs, triggerName) +import Copilot.Interpret.Eval (ExecTrace, Output, interpObservers, + interpTriggers) + +-- * Abstract representation of a trace + +-- | Data related to a specification. +data AppData = AppData + { adTraceElems :: Trace + , adLastSample :: Int + } + deriving (Generic, Show) + +instance ToJSON AppData + +-- | A trace, defined by a list of trace elements, each defining a stream (in +-- an abstract sense). +type Trace = [ TraceElem ] + +-- | An element of a trace, or stream, with a name, its values, and information +-- about its type, needed to render it correctly. +data TraceElem = TraceElem + { teName :: String + , teIsBoolean :: Bool + , teIsFloat :: Bool + , teValues :: [ TraceValue ] + } + deriving (Generic, Show) + +instance ToJSON TraceElem + +-- | An individual sample, with a value and an indication of whether the sample +-- exists. +data TraceValue = TraceValue + { tvValue :: String + , tvIsEmpty :: Bool + } + deriving (Generic, Show) + +instance ToJSON TraceValue + +-- | Generate an abstract representation of a trace of a specification +-- interpreted for a given number of steps. +makeTraceEval :: Int + -> Spec + -> ExecTrace + -> AppData +makeTraceEval k spec e = + AppData (observerTEs ++ triggerTEs) (k - 1) + where + observerTEs = map mkTraceElem (interpObserversOpt spec e) + triggerTEs = map mkTraceElem (interpTriggersWithArgs spec e) + +-- | Generate an element of a trace from a stream name and its values over +-- time. +mkTraceElem :: (String, [Maybe Output]) -> TraceElem +mkTraceElem (name, outputs) = TraceElem + { teName = name + , teValues = values + , teIsBoolean = any (isBoolean . tvValue) values + , teIsFloat = any (isFloat . tvValue) values + } + where + values = map mkTraceValue outputs + +-- | Generate an individual value (sample) of a trace. +mkTraceValue :: Maybe Output -> TraceValue +mkTraceValue x = TraceValue (showValue x) (isNothing x) + +-- * Auxiliary functions + +-- | Compute the list of values associated to observers. +interpObserversOpt :: Spec -> ExecTrace -> [(String, [Maybe Output])] +interpObserversOpt _spec = map (second (map Just)) . interpObservers + +-- | Compute the list of values associated to triggers and their arguments. +-- +-- For each trigger, we first include the values of the trigger itself, and +-- then the values of the arguments to the trigger. +interpTriggersWithArgs :: Spec -> ExecTrace -> [(String, [Maybe Output])] +interpTriggersWithArgs spec = concatMap triggerOutputs . interpTriggers + where + -- This function adds one more output for the trigger itself. + triggerOutputs :: (String, [Maybe [Output]]) -> [(String, [Maybe Output])] + triggerOutputs (triggerName, triggerArgs) = + (triggerName, triggerValues) : zip argNames argValues + where + triggerValues = map triggerValue triggerArgs + + -- Value for the trigger at a given time, based on the values of its + -- arguments. + triggerValue :: Maybe [Output] -> Maybe Output + triggerValue Nothing = Just "false" + triggerValue (Just _) = Just "true" + + -- Names and values for the arguments. + argNames = map (\ix -> triggerName ++ "Arg" ++ show ix) [0..] + argValues = transpose (transMaybes numArgs triggerArgs) + numArgs = triggerNumArgs spec triggerName + +-- Number of arguments to a trigger in a spec. +-- +-- PRE: name exists as a trigger in spec. +triggerNumArgs :: Spec -> String -> Int +triggerNumArgs spec name = + case find (\t -> triggerName t == name) (specTriggers spec) of + Nothing -> error "Couldn't find given trigger in spec, should never occur!" + Just t -> length $ triggerArgs t + +-- | True if the input value denotes a boolean value. +isBoolean :: String -> Bool +isBoolean "true" = True +isBoolean "false" = True +isBoolean _ = False + +-- | True if the input value denotes a floating point value. +isFloat :: String -> Bool +isFloat s = isJust asInt || isJust asFloat + where + asInt :: Maybe Int + asInt = readMaybe s + + asFloat :: Maybe Float + asFloat = readMaybe s + +-- | Show a value. +showValue :: Maybe Output -> String +showValue Nothing = "--" +showValue (Just s) | isFloat s = showValueFloat s + | otherwise = s + +-- | Show a floating point value. +showValueFloat :: Output -> String +showValueFloat = formatFloat . read + where + formatFloat :: Double -> String + formatFloat = printf "%.2g" + +-- | Given a list of maybe lists of known length, this function creates a list +-- of lists, pushing the Maybe's inside. +transMaybes :: Int -> [Maybe [a]] -> [[Maybe a]] +transMaybes = map . transMaybes' + where + transMaybes' argsLength (Just xs) = map Just xs + transMaybes' argsLength Nothing = replicate argsLength Nothing From 93df3925b06c4ba0c60ae58c6b67becfeacc9ca6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 27 Jun 2025 13:26:16 +0000 Subject: [PATCH 198/483] copilot-visualizer: Remove duplicate .gitignore. Refs #624. The copilot-visualizer package was developed outside of this repo, but there's interest in having all Copilot projects and libraries merged into a main repo for ease of management. A prior commit add the copilot-visualizer library as a subdirectory of the main repository. The library included a .gitignore file, which is a duplicate of the one already present at the top level of the repository. This commit removes the duplicate file to have all ignored files governed by the .gitignore file at the top level. --- copilot-visualizer/.gitignore | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 copilot-visualizer/.gitignore diff --git a/copilot-visualizer/.gitignore b/copilot-visualizer/.gitignore deleted file mode 100644 index f94ebc47b..000000000 --- a/copilot-visualizer/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -dist -dist-* -cabal-dev -*.o -*.hi -*.chi -*.chs.h -*.dyn_o -*.dyn_hi -.hpc -.hsenv -.cabal-sandbox/ -cabal.sandbox.config -*.prof -*.aux -*.hp -*.eventlog -.stack-work/ -cabal.project.local -cabal.project.local~ -.HTF/ -.ghc.environment.* -copilot-profiling -.DS_Store -.log From 398f75d4643f97c4b7aa1f5ade8473de96723838 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 27 Jun 2025 13:29:31 +0000 Subject: [PATCH 199/483] copilot-visualizer: Adjust issue numbers in CHANGELOG. Refs #624. The copilot-visualizer package was originally developed in an external repo, but there is interest in having all Copilot projects and libraries merged into the main repo for ease of management. A prior commit added the copilot-visualizer library as a subdirectory of the main copilot repo. However the CHANGELOG included with that library lists issue numbers that refer to issues in the original repo, not the main Copilot repo, which can cause confusion. This commit adjusts old issue numbers in the copilot-visualizer CHANGELOG to include the package name, to make it clear that they refer to the old repo. --- copilot-visualizer/CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 44cb9f05e..0c219f99d 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,3 @@ 2025-06-26 - * Release 4.4. (#5) - * Initial version. (#4) + * Release 4.4. (copilot-visualizer#5) + * Initial version. (copilot-visualizer#4) From b38f158b8a9ff437bd8458e3f6981ab59b69cf78 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 27 Jun 2025 13:38:11 +0000 Subject: [PATCH 200/483] copilot-visualizer: Document changes in CHANGELOG. Refs #624. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 0c219f99d..b2058f089 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2025-06-27 + * Include in mainline. (#624) + 2025-06-26 * Release 4.4. (copilot-visualizer#5) * Initial version. (copilot-visualizer#4) From 1e24c308af55b0f01bb33af2334e74faba337918 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 28 Jun 2025 14:01:04 +0000 Subject: [PATCH 201/483] copilot-core: Remove deprecated fields of Copilot.Core.Expr.UExpr. Refs #636. The record fields uExprExpr and uExprType, part of Copilot.Core.Expr, are never really used within the module or in any other part of Copilot or, as far as we know, by any user of Copilot. The record fields were deprecated in Copilot 4.2 and no messages have been received requesting that they be kept in this library. As per our internal policy of waiting 3 versions from deprecation until a public interface declaration can be removed, these definitions can now be removed. This commit removes the fields uExprType and uExprExpr from the definition of Copilot.Core.Expr.UExpr, making the type no longer a record. --- copilot-core/src/Copilot/Core/Expr.hs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Expr.hs b/copilot-core/src/Copilot/Core/Expr.hs index 277e919d1..b65126a04 100644 --- a/copilot-core/src/Copilot/Core/Expr.hs +++ b/copilot-core/src/Copilot/Core/Expr.hs @@ -67,8 +67,4 @@ data Expr a where -- | A untyped expression that carries the information about the type of the -- expression as a value, as opposed to exposing it at type level (using an -- existential). -data UExpr = forall a . Typeable a => UExpr - { uExprType :: Type a - , uExprExpr :: Expr a - } -{-# DEPRECATED uExprType, uExprExpr "These fields are deprecated in Copilot 4.2. Use pattern matching instead." #-} +data UExpr = forall a . Typeable a => UExpr (Type a) (Expr a) From 2f4cc73a5f6db04fdd3bbabca6e52d35d011836e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 28 Jun 2025 14:05:52 +0000 Subject: [PATCH 202/483] copilot-core: Document changes in CHANGELOG. Refs #636. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 4a0902c21..edd45681e 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2025-06-28 + * Remove deprecated fields of Copilot.Core.Expr.UExpr. (#636) + 2025-05-07 * Version bump (4.4). (#618) * Remove deprecated Copilot.Core.Type.uTypeType. (#615) From 282a78fe127040aa77d042b01fa21b868e8dc7e4 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:01:55 -0700 Subject: [PATCH 203/483] copilot-theorem: Remove 'GeneralizedNewtypeDeriving' unused pragma from 'what4.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.What4. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 818ac9b3a..c2c7eb01b 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -1,18 +1,17 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} -- | -- Module : Copilot.Theorem.What4 From ad38ba44ee3e45ce0a196e5f4e78d981b3943236 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:04:04 -0700 Subject: [PATCH 204/483] copilot-theorem: Remove 'MultiWayIf' unused pragma from 'what4.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.What4. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index c2c7eb01b..7c7e84233 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -3,7 +3,6 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} From 4f5f09adc31500cfd30950d31de3d7a6d4180f76 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:05:14 -0700 Subject: [PATCH 205/483] copilot-theorem: Remove 'StandaloneDeriving' unused pragma from 'what4.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.What4. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 7c7e84233..8610571c0 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -6,7 +6,6 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} From 61fa0b46cfd7fec8964b328fc9625243e6fa412e Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:07:14 -0700 Subject: [PATCH 206/483] copilot-theorem: Remove 'TemplateHaskell' unused pragma from 'what4.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.What4. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 8610571c0..c9da7fe8b 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -6,7 +6,6 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} From 3088023b5e781b65afb8f4c96d93085c4a17b7fd Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:08:44 -0700 Subject: [PATCH 207/483] copilot-theorem: Remove 'TupleSections' unused pragma from 'what4.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.What4. --- copilot-theorem/src/Copilot/Theorem/What4.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index c9da7fe8b..51ed6e8ff 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -6,7 +6,6 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} From bbb90b6c8fa777dc2cb088900a5ba5456bac8a49 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:10:00 -0700 Subject: [PATCH 208/483] copilot-theorem: Remove 'LambdaCase' unused pragma from 'Prover.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.Kind2.Prover. --- copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs index 66db2c346..4bb52322e 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE Trustworthy #-} -- | A prover backend based on Kind2. From 2d101a7398ae1cc86d4d00f66b4ad0b96f702b84 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 07:11:08 -0700 Subject: [PATCH 209/483] copilot-theorem: Remove 'LambdaCase' unused pragma from 'SMTIO.hs'. Refs #613. The code of copilot-theorem contains unused language pragmas. To comply with our coding style, which required unused code not to be included in the codebase, those pragmas should be removed. This commit removes an unused pragma from Copilot.Theorem.Prover.SMTIO. --- copilot-theorem/src/Copilot/Theorem/Prover/SMTIO.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Prover/SMTIO.hs b/copilot-theorem/src/Copilot/Theorem/Prover/SMTIO.hs index 2ce606b31..3035f718e 100644 --- a/copilot-theorem/src/Copilot/Theorem/Prover/SMTIO.hs +++ b/copilot-theorem/src/Copilot/Theorem/Prover/SMTIO.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Safe #-} From c9954d663f2988b2db9bd8ce65f9f14e71512195 Mon Sep 17 00:00:00 2001 From: carte731 Date: Thu, 29 May 2025 09:24:36 -0700 Subject: [PATCH 210/483] copilot-theorem: Document changes in CHANGELOG. Refs #613. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index f221aec44..d28cc3041 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2025-05-29 + * Removed unused pragmas. (#613) + 2025-05-07 * Version bump (4.4). (#618) * Translate quantifiers correctly in Kind2 backend. (#594) From 1152a8f4dea8a7afd6b6cca09e71f5c7e11b238f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 6 Jul 2025 01:25:41 +0000 Subject: [PATCH 211/483] copilot-theorem: Relax version constraint on `what4`. Refs #611. what4 has seen a new release 1.7, but copilot-theorem needs versions strictly lower than 1.7. This version is API-compatible for all features used by copilot-theorem. --- copilot-theorem/copilot-theorem.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index ad7ab8699..50f777f49 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -61,7 +61,7 @@ library , random >= 1.1 && < 1.3 , transformers >= 0.5 && < 0.7 , xml >= 1.3 && < 1.4 - , what4 >= 1.3 && < 1.7 + , what4 >= 1.3 && < 1.8 , copilot-core >= 4.4 && < 4.5 , copilot-prettyprinter >= 4.4 && < 4.5 From 1f68f7e9d5f09a2a0e6cdd902cfd20d637c30fef Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 6 Jul 2025 01:28:06 +0000 Subject: [PATCH 212/483] copilot-theorem: Document changes in CHANGELOG. Refs #611. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index d28cc3041..c0a0a02bd 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ -2025-05-29 +2025-07-05 * Removed unused pragmas. (#613) + * Relax version constraint on what4. (#611) 2025-05-07 * Version bump (4.4). (#618) From a9545d72df7839a5bbb999a500278c52d8d19a86 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 8 Jul 2025 06:39:31 +0000 Subject: [PATCH 213/483] Bump versions and constraints. Refs #642. --- copilot-bluespec/copilot-bluespec.cabal | 4 ++-- copilot-c99/copilot-c99.cabal | 4 ++-- copilot-core/copilot-core.cabal | 2 +- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- copilot-language/copilot-language.cabal | 8 ++++---- copilot-libraries/copilot-libraries.cabal | 4 ++-- .../copilot-prettyprinter.cabal | 4 ++-- copilot-theorem/copilot-theorem.cabal | 6 +++--- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ copilot/copilot.cabal | 14 +++++++------- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 6509e7e2a..90d9f89d1 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.4 +version : 4.5 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -44,7 +44,7 @@ library , filepath >= 1.4 && < 1.6 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.4 && < 4.5 + , copilot-core >= 4.5 && < 4.6 , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index dabf04bf0..17ec0d950 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.4 +version : 4.5 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.4 && < 4.5 + , copilot-core >= 4.5 && < 4.6 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index daab8383b..ffd4031a9 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.4 +version: 4.5 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index d5b679829..4bcfb3138 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.4 +version: 4.5 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.4 && < 4.5 + copilot-core >= 4.5 && < 4.6 exposed-modules: diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 87d54101d..e9d4de9a9 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.4 +version: 4.5 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.4 && < 4.5 - , copilot-interpreter >= 4.4 && < 4.5 - , copilot-theorem >= 4.4 && < 4.5 + , copilot-core >= 4.5 && < 4.6 + , copilot-interpreter >= 4.5 && < 4.6 + , copilot-theorem >= 4.5 && < 4.6 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 078d009fd..214fe7dc4 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.4 +version: 4.5 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.4 && < 4.5 + , copilot-language >= 4.5 && < 4.6 exposed-modules: Copilot.Library.Libraries diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 2a4a9578f..2953cfef6 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.4 +version: 4.5 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.4 && < 4.5 + copilot-core >= 4.5 && < 4.6 exposed-modules: diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 50f777f49..47cd076b0 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.4 +version : 4.5 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.4 && < 4.5 - , copilot-prettyprinter >= 4.4 && < 4.5 + , copilot-core >= 4.5 && < 4.6 + , copilot-prettyprinter >= 4.5 && < 4.6 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index f7151dfeb..8b915e154 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.4 +Version: 4.5 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.4 && < 4.5, - copilot-core >= 4.4 && < 4.5, - copilot-theorem >= 4.4 && < 4.5, + copilot-c99 >= 4.5 && < 4.6, + copilot-core >= 4.5 && < 4.6, + copilot-theorem >= 4.5 && < 4.6, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.4 && < 4.5, - copilot-language >= 4.4 && < 4.5, - copilot-libraries >= 4.4 && < 4.5, - copilot-prettyprinter >= 4.4 && < 4.5, + copilot >= 4.5 && < 4.6, + copilot-language >= 4.5 && < 4.6, + copilot-libraries >= 4.5 && < 4.6, + copilot-prettyprinter >= 4.5 && < 4.6, copilot-verifier exposed-modules: Copilot.Verifier.Examples diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index e57efd780..f0f59c93d 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.4 +version: 4.5 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.4 && < 4.5, - copilot-core >= 4.4 && < 4.5, - copilot-interpreter >= 4.4 && < 4.5, - copilot-language >= 4.4 && < 4.5 + copilot >= 4.5 && < 4.6, + copilot-core >= 4.5 && < 4.6, + copilot-interpreter >= 4.5 && < 4.6, + copilot-language >= 4.5 && < 4.6 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.4 && < 4.5, + copilot >= 4.5 && < 4.6, copilot-visualizer if flag(examples) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 6401ef883..6d9570b19 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.4 +version: 4.5 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.4 && < 4.5 - , copilot-theorem >= 4.4 && < 4.5 - , copilot-language >= 4.4 && < 4.5 - , copilot-libraries >= 4.4 && < 4.5 - , copilot-c99 >= 4.4 && < 4.5 - , copilot-prettyprinter >= 4.4 && < 4.5 + , copilot-core >= 4.5 && < 4.6 + , copilot-theorem >= 4.5 && < 4.6 + , copilot-language >= 4.5 && < 4.6 + , copilot-libraries >= 4.5 && < 4.6 + , copilot-c99 >= 4.5 && < 4.6 + , copilot-prettyprinter >= 4.5 && < 4.6 exposed-modules: Language.Copilot, Language.Copilot.Main From 3d8674a71e97dcededf6fcc21f2402f4cbc5098d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 8 Jul 2025 06:49:34 +0000 Subject: [PATCH 214/483] Document changes in CHANGELOG. Refs #642. --- copilot-bluespec/CHANGELOG | 3 ++- copilot-c99/CHANGELOG | 3 +++ copilot-core/CHANGELOG | 3 ++- copilot-interpreter/CHANGELOG | 3 +++ copilot-language/CHANGELOG | 3 +++ copilot-libraries/CHANGELOG | 3 +++ copilot-prettyprinter/CHANGELOG | 3 +++ copilot-theorem/CHANGELOG | 3 ++- copilot-verifier/CHANGELOG | 3 ++- copilot-visualizer/CHANGELOG | 3 ++- copilot/CHANGELOG | 3 +++ 11 files changed, 28 insertions(+), 5 deletions(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 10f4486ec..ccfaad8ab 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2025-05-18 +2025-07-07 + * Version bump (4.5). (#642) * Include `copilot-bluespec` in mainline `copilot` repo. (#623) 2025-05-08 diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 00f705df8..7b7d3aeb6 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index edd45681e..810d39b24 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2025-06-28 +2025-07-07 + * Version bump (4.5). (#642) * Remove deprecated fields of Copilot.Core.Expr.UExpr. (#636) 2025-05-07 diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index c5f4894c8..cd1e6b431 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index fc28f01f1..60112d6f8 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index a0c598f54..c724f29b5 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 360d2e9a0..8f94d9fa6 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index c0a0a02bd..da078ea43 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2025-07-05 +2025-07-07 + * Version bump (4.5). (#642) * Removed unused pragmas. (#613) * Relax version constraint on what4. (#611) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 03bd15246..59ac57d9a 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,4 +1,5 @@ -2025-06-20 +2025-07-07 + * Version bump (4.5). (#642) * Include `copilot-verifier` in mainline `copilot` repo. (#622) 2025-05-08 diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index b2058f089..91c4b41fd 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,4 +1,5 @@ -2025-06-27 +2025-07-07 + * Version bump (4.5). (#642) * Include in mainline. (#624) 2025-06-26 diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index b38dd2c3d..e92d2fe91 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-07-07 + * Version bump (4.5). (#642) + 2025-05-07 * Version bump (4.4). (#618) * Update build status badge URL. (#612) From 0a1612699cb7c559b504a80addb676a15d6ee0bb Mon Sep 17 00:00:00 2001 From: Sukhman Kahlon Date: Fri, 15 Aug 2025 15:08:13 -0700 Subject: [PATCH 215/483] copilot: Add hello world example for FPGA. Refs #657. We need an example that demonstrates how to work with FPGAs using Copilot. This will help users start with copilot-bluespec, and also help validate our work. This commit adds a hello world example that demonstrates how to control 4 LEDs from 4 switches. The tutorial describes how to install Vivado, create the spec, compile it (in bluespec and then verilog), and upload it to the board. --- .../examples/fpga/HelloWorld/HelloWorld.hs | 19 + copilot/examples/fpga/HelloWorld/README.md | 557 ++++++++++++++++++ copilot/examples/fpga/HelloWorld/Top.bs | 17 + .../examples/fpga/HelloWorld/constraints.xdc | 17 + copilot/examples/fpga/HelloWorld/mkTop.v | 75 +++ 5 files changed, 685 insertions(+) create mode 100644 copilot/examples/fpga/HelloWorld/HelloWorld.hs create mode 100644 copilot/examples/fpga/HelloWorld/README.md create mode 100644 copilot/examples/fpga/HelloWorld/Top.bs create mode 100644 copilot/examples/fpga/HelloWorld/constraints.xdc create mode 100644 copilot/examples/fpga/HelloWorld/mkTop.v diff --git a/copilot/examples/fpga/HelloWorld/HelloWorld.hs b/copilot/examples/fpga/HelloWorld/HelloWorld.hs new file mode 100644 index 000000000..c694c8f2e --- /dev/null +++ b/copilot/examples/fpga/HelloWorld/HelloWorld.hs @@ -0,0 +1,19 @@ +module Main where + +import Language.Copilot +import Copilot.Compile.Bluespec + +-- Define switches input +sw :: Stream Word8 +sw = extern "sw" Nothing + +-- Define LEDS output +leds :: Stream Word8 +leds = sw + +spec = do + trigger "leds" true [arg leds] + +main = do + spec' <- reify spec + compile "HelloWorld" spec' diff --git a/copilot/examples/fpga/HelloWorld/README.md b/copilot/examples/fpga/HelloWorld/README.md new file mode 100644 index 000000000..78ecd1aa8 --- /dev/null +++ b/copilot/examples/fpga/HelloWorld/README.md @@ -0,0 +1,557 @@ +# Hello World Example on an FPGA Board + +Copilot is a programming language for hard real-time systems. + +This tutorial shows how to use Copilot to control LEDs from hardware switches. +The demonstration serves as an introduction of the complete toolchain needed to +integrate Copilot into hardware projects. The specification is verified via the +onboard LEDs on an Artix-7 XC7A35T FPGA, using AMD/Xilinx Vivado Design Suite. + +This page lists all the necessary applications and auxiliary files required to +run Copilot onto an FPGA. Specifically, this page shows: + +- How to install the necessary dependencies. +- How to create Copilot code that invokes the Bluespec backend. +- How to compile the Bluespec files into Verilog. +- How to run and test the design on an FPGA board. + +## Table of Contents + +- [Installation](#installation) +- [Example](#example) + - [Hello World Code](#hello-world-code) + - [Compiling Copilot into Bluespec](#compiling-copilot-into-bluespec) + - [Compiling Bluespec to Verilog](#compiling-bluespec-to-verilog) +- [Running the Hello World Example on Vivado](#running-the-hello-world-example-on-vivado) +- [Acknowledgements](#acknowledgments) + +# Installation +[(Back to top)](#table-of-contents) + +## Vivado 2025.1 + +Vivado can be installed through terminal as suggested by the following process. +The steps provided are for a Vivado version that is ~50 GB less than the one +directly available on the AMD website. If issues arise, see the official +[Vivado +documentation](https://docs.amd.com/r/en-US/ug910-vivado-getting-started/Installing-the-Vivado-Design-Suite) +for an alternative installation method. + +1. Create an account on the +[AMD](https://www.amd.com/en/registration/create-account.html) website, which +allows downloading the Vivado Design Suite. + +2. Download the [Vivado +Edition 2025.1](https://www.xilinx.com/member/forms/download) +specific for your device and verify the information required by AMD. + +3. Open the command terminal, navigate to the directory containing the + downloaded Vivado installation file, and adjust the permissions of the file + so that it can be executed: + + ```sh + $ chmod a+x FPGAs_AdaptiveSoCs_Unified_SDI_2025.1_0530_0145_.bin + ``` + Replace `` with your operating system. + +4. Run the installation program: + + ```sh + $ ./FPGAs_AdaptiveSoCs_Unified_SDI_2025.1_0530_0145_.bin + ``` + +5. An installation window will pop up. Follow the installation prompt. When + the window *Select Product to Install* is shown, select the product + *Vivado*. Then, select the *Vivado ML Standard* edition from the two + options. In the *Vivado ML Standard* window, in the subcategory *Devices*, + select the FPGA board that you're working with (in our case the *7 Series*). + Hit *install* when prompted. The download should be ~20 GB to 50 GB. + +6. Ensure that the directory where you choose to install Vivado has + read/write permissions. If the candidate installation + directory is highlighted red, it means it has the wrong permissions and/or + owner. If that happens, change the owner into a different user, or try to + run the binary installer as sudo (root) by running the command: + ```sh + $ sudo ./FPGAs_AdaptiveSoCs_Unified_SDI_2025.1_0530_0145_.bin + ``` + + The rest of the instructions assume you have installed Vivado under + `/tools`. Adjust paths accordingly as needed. + +7. Install the drivers with: + + ```sh + $ source /tools/Xilinx/2025.1/Vivado/.settings64-Vivado.sh + $ sudo /tools/Xilinx/2025.1/data/xicom/cable_drivers//install_script/install_drivers/install_drivers + ``` + + The terminal should show a message indicating that the drivers were + installed successfully. A sample output is included below. Note that the + installation path may be different on your computer. + +
Terminal Output (click to expand) + + ``` + INFO: Installing cable drivers. + INFO: Script name = /tools/Xilinx/2025.1/data/xicom/cable_drivers/lin64/install_script/install_drivers/install_drivers + INFO: HostName = iron-B650M-C-V2-Y1 + INFO: RDI_BINROOT= /tools/Xilinx/2025.1/data/xicom/cable_drivers/lin64/install_script/install_drivers + INFO: Current working dir = /home/iron/Downloads + INFO: Kernel version = 6.8.0-60-generic. + INFO: Arch = x86_64. + Successfully installed Digilent Cable Drivers + --File /etc/udev/rules.d/52-xilinx-ftdi-usb.rules does not exist. + --File version of /etc/udev/rules.d/52-xilinx-ftdi-usb.rules = 0000. + --Updating rules file. + --File /etc/udev/rules.d/52-xilinx-pcusb.rules does not exist. + --File version of /etc/udev/rules.d/52-xilinx-pcusb.rules = 0000. + --Updating rules file. + + INFO: Digilent Return code = 0 + INFO: Xilinx Return code = 0 + INFO: Xilinx FTDI Return code = 0 + INFO: Return code = 0 + INFO: Driver installation successful. + CRITICAL WARNING: Cable(s) on the system must be unplugged then plugged back in order for the driver scripts to update the cables. + ``` +
+ +8. Verify that Vivado has been installed on your device by running the + following command and checking that you see version information + being printed: + + ```sh + $ vivado -version + vivado v2025.1 (64-bit) + Tool Version Limit: 2025.05 + SW Build 6140274 on Wed May 21 22:58:25 MDT 2025 + IP Build 6138677 on Thu May 22 03:10:11 MDT 2025 + SharedData Build 6139179 on Tue May 20 17:58:58 MDT 2025 + Copyright 1986-2022 Xilinx, Inc. All Rights Reserved. + Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. + ``` + +# Example + +## Hello World Code +[(Back to top)](#table-of-contents) + +This example shows how Copilot can be used to control LEDs using switches. We +use four switches and four LEDs available on the FPGA board. + +The main Copilot specification is created in a file `HelloWorld.hs`. The final +contents of that file is available in the [Copilot +repository](https://github.com/Copilot-Language/copilot/tree/master/copilot/examples/fpga/HelloWorld/HelloWorld.hs). +The same directory contains any auxiliary files needed to run the code onto an +FPGA board. + +The first definition in a Copilot specification is a module declaration, which +should always refer to the module as the `Main` module: + +```haskell +module Main where +``` + +Next, we import the main definitions of the Copilot language, as well as the +Bluespec backend: + +```haskell +import Copilot.Compile.Bluespec +import Language.Copilot +``` + +Copilot normally represents data as infinite successions of values or +*streams*. Streams whose values are provided by input devices are considered +external inputs for this specification or *externs*. Because switches are +inputs to the Copilot logic, we define the current value of the state of the +switches as an extern carrying values of typed 8-bit unsigned integer, or +`Word8`: + +```haskell +sw :: Stream Word8 +sw = extern "sw" Nothing +``` + +The four least-significant bits of the current value of `sw` represent the +current state of each of the four switches on the board, respectively. + +Next, we define the current value of the four LEDs in a similar way. To make +the state of the LEDs match that of the switches, we state that their values +are the same: + +```haskell +leds :: Stream Word8 +leds = sw +``` + +To connect the current values of the LEDs to the actual LEDs on the board, we +must communicate the current values as outputs of the specification which we do +with a *trigger*: + +```haskell +spec = do + trigger "leds" true [arg leds] +``` + +In the above trigger definition, `"leds"` is the name of the output, whereas +`leds` (without double quotes) is a Copilot stream that, at each point in time, +will contain the current values that the LEDs should have. + +We instruct Copilot to compile the file and generate Bluespec when the Copilot +spec is executed: + +```haskell +main = do + spec' <- reify spec + compile "HelloWorld" spec' +``` + +## Compiling Copilot into Bluespec +[(Back to top)](#table-of-contents) + +Copilot and Bluespec compiler must be installed on the device (see +[Copilot](https://github.com/Copilot-Language/copilot) and +[Bluespec](https://github.com/Copilot-Language/copilot-bluespec) for +installation instructions). + +In the terminal, navigate to the directory that has the `HelloWorld.hs` +example. Compile the Copilot specification into Bluespec by running: + +```shell +$ runhaskell HelloWorld.hs +``` + +This command generates three Bluespec files in the same directory: +`HelloWorld.bs`, `HelloWorldIfc.bs` and `HelloWorldTypes.bs`. These files make +up the internal logic and interface in Verilog. For reference, the expected +content of these files is included below. The specific content may change +slightly across versions of Copilot. + +
+Contents of generated Bluespec files (click to expand) + +**`HelloWorld.bs`** + +```bluespec +package HelloWorld where { +import FloatingPoint; + +import Vector; + +import HelloWorldTypes; + +import HelloWorldIfc; + +import BluespecFP; + +mkHelloWorld :: Prelude.Module HelloWorldIfc -> + Prelude.Module Prelude.Empty; +mkHelloWorld ifcMod = + module { + ifc <- ifcMod; + let { leds_0_guard :: Prelude.Bool; + leds_0_guard = Prelude.True; + leds_0_arg0 :: Prelude.UInt 8; + leds_0_arg0 = ifc.sw._read; }; + rules { + + "leds_0": when leds_0_guard ==> ifc.leds leds_0_arg0 + } + }; +} +``` + +**`HelloWorldIfc.bs`** + +```bluespec +package HelloWorldIfc where { +import FloatingPoint; + +import Vector; + +import HelloWorldTypes; + +interface HelloWorldIfc = { + leds :: Prelude.UInt 8 -> Prelude.Action; + sw :: Prelude.Reg (Prelude.UInt 8) +} +} +``` + +**`HelloWorldTypes.bs`** + +```bluespec +package HelloWorldTypes where { +import FloatingPoint; + +import Vector +} +``` +
+ +In the same directory, create a new file named `Top.bs`. This file serves as +top-level Bluespec module, connects the three generated files together and +contains the interface definitions. This file will later be compiled into +Verilog and deployed onto the FPGA. + +We define both the `helloWorldIfc` and `mkTop` modules. When `HelloWorld.bs` is +compiled, it makes `leds` an output, which translates to an *action* method in +Bluespec. Since an *action* method updates state rather than directly driving a +signal, we need a way to hold its value, so we create the register `led_reg`, +which stores the LEDs' value. The *type* of `led_reg` must match the *type* of +`leds` in `HelloWorldIfc.bs`. + +```bluespec +package Top where + + import HelloWorld + import HelloWorldIfc + import HelloWorldTypes + + helloWorldIfc :: Prelude.Module HelloWorldIfc + helloWorldIfc = + module + -- Register that connects the LEDs to values produced by Copilot + led_reg :: Prelude.Reg (Prelude.UInt 8) <- mkReg 0 + interface + leds reg = action + led_reg := reg + + mkTop :: Prelude.Module Empty + mkTop = mkHelloWorld helloWorldIfc +``` +## Compiling Bluespec to Verilog +[(Back to top)](#table-of-contents) + +We use the Bluespec compiler to generate Verilog output using the top-level +module. `mkTop` is a function in the `Top.bs` file that instantiates the +top-level module of the design. In the terminal, we run the following command: + +```shell +$ bsc -verilog -g mkTop -u Top.bs +``` + +This step generates the Verilog code that can be compiled to run onto the FPGA +board. We can ignore the warnings shown in terminal output, but we must ensure +that the file `mkTop.v` is successfully created in the current directory. + +To program the code onto the FPGA, the ports declarations and assignments need +modifications. In the `Top.bs` file, the module that had the register and +interface for the LEDs and switches is defined in `helloWorldIfc`. Due to this, +when compiling into Verilog, the Top level module does not have `sw` and `leds` +defined in its port declaration. This has to be modified manually. + +The following `mkTop.v` lines list the module ports in the `Top.bs` file. These +are the external signals that connect to this module: + +```verilog +module mkTop(CLK, + RST_N); + input CLK; + input RST_N; +``` + +We modify `mkTop` and add the input port `sw` and output port `leds` inside the +module port because these signals will externally connect to the FPGA. The FPGA +is limited to 4 LEDs and 4 switches. Alongside the existing definitions, we +need to *assign* the value of `ifc_led_reg$D_IN`, which is an internal wire +created by the Verilog code to the output `leds`. We *assign* the output `leds` +to the register. This register was defined in our `Top.bs` and appears as an +internal register `ifc_led_reg` and its current contents appears as the value +in `leds`. The value is updated on every rising edge of the `CLK`. + +For reference, the complete contents of the modified `mkTop.v` is included +below. + +
+Contents of modified `mkTop.v` file (click to expand) + +```verilog +// +// Generated by Bluespec Compiler, version 2025.01.1 (build 65e3a87a) +// +// Further modified by Sukhman Kahlon. +// +// On Thu Aug 14 14:52:29 PDT 2025 +// +// +// Ports: +// Name I/O size props +// CLK I 1 clock +// RST_N I 1 reset +// +// No combinational paths from inputs to outputs +// +// + +`ifdef BSV_ASSIGNMENT_DELAY +`else + `define BSV_ASSIGNMENT_DELAY +`endif + +`ifdef BSV_POSITIVE_RESET + `define BSV_RESET_VALUE 1'b1 + `define BSV_RESET_EDGE posedge +`else + `define BSV_RESET_VALUE 1'b0 + `define BSV_RESET_EDGE negedge +`endif + +module mkTop(CLK, + RST_N, + sw, + leds); + input CLK; + input RST_N; + input [3:0] sw; + output [3:0] leds; + + assign leds[3:0] = ifc_led_reg[3:0]; + + // register ifc_led_reg + reg [7 : 0] ifc_led_reg; + wire [7 : 0] ifc_led_reg$D_IN; + wire ifc_led_reg$EN; + + // register ifc_led_reg + assign ifc_led_reg$D_IN = sw[3:0]; + assign ifc_led_reg$EN = 1'd1 ; + + // handling of inlined registers + + always@(posedge CLK) + begin + if (RST_N == `BSV_RESET_VALUE) + begin + ifc_led_reg <= `BSV_ASSIGNMENT_DELAY 8'd0; + end + else + begin + if (ifc_led_reg$EN) + ifc_led_reg <= `BSV_ASSIGNMENT_DELAY ifc_led_reg$D_IN; + end + end + + // synopsys translate_off + `ifdef BSV_NO_INITIAL_BLOCKS + `else // not BSV_NO_INITIAL_BLOCKS + initial + begin + ifc_led_reg = 8'hAA; + end + `endif // BSV_NO_INITIAL_BLOCKS + // synopsys translate_on +endmodule // mkTop +``` +
+ +This code will be copied to Vivado and replace the default empty module created +by Vivado. + +To interface between the FPGA board and the code, a constraints file needs to +be created and is dependent on the type of FPGA board used. Please refer to the +reference manual for your specific FPGA board, to assign pins for LEDs +accordingly. Digilent offers I/O pins for the respective boards on the +[Digilent Github Repository](https://github.com/Digilent/digilent-xdc). + +Ensure the name of the external pins after `get_ports` matches the inputs / +outputs created in the port declaration. In this example, `CLK`, `RST_N`, +`leds`, and `sw` *must* be assigned. For reference, here is the constraints +file assigning LED 4-7 for the Arty 35T CSG324-1 to the output `leds`, and +setting the switches and period accordingly. + +
+Contents of constraints file `new_constraints.xdc` (click to expand) + +```xdc +set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports CLK] +create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK] + +set_property PACKAGE_PIN C2 [get_ports RST_N] +set_property IOSTANDARD LVCMOS33 [get_ports RST_N] + +# Switches +set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L12N_T1_MRCC_16 Sch=sw[0] +set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L13P_T2_MRCC_16 Sch=sw[1] +set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2] +set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3] + +# LEDs +set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { leds[0] }]; #IO_L24N_T3_35 Sch=led[4] +set_property -dict { PACKAGE_PIN J5 IOSTANDARD LVCMOS33 } [get_ports { leds[1] }]; #IO_25_35 Sch=led[5] +set_property -dict { PACKAGE_PIN T9 IOSTANDARD LVCMOS33 } [get_ports { leds[2] }]; #IO_L24P_T3_A01_D17_14 Sch=led[6] +set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { leds[3] }]; #IO_L24N_T3_A00_D16_14 Sch=led[7] +``` +
+ +# Running the Hello World Example on Vivado +[(Back to top)](#table-of-contents) + +Once installed, open the Vivado Design Suite. + +1. Click *Create Project* > *Next* > *Project Name:* `HelloWorld` and choose +the desired location. + +2. Select *Create Project Subdirectory* > *Next* > *RTL Project* > *Next*. + +3. Select *Do not specify sources at this time* > *Next*. + +4. Search for the board / chip. In this example, the board used is +`xc7a35tcsg324-1`. The project should be created and initialized. + +6. Once the Vivado workspace opens, under the *Sources* tab, select *+* > *Add +or create design sources* > *Next* > *Create File*. + +7. Introduce the following values: + - *File Type*: `Verilog` + - *File Name*: `mkTop` + - *File Location*: `` + +8. Under the *Sources* tab, select *+* > *Add or create constraints* > *Next* > + *Create File*. + +10. Introduce the following values: + - *File Type*: `XDC` + - *File Name*: `new_constraints` + - *File Location*: `` + +11. From the *Sources* drop down, select *Design Sources* and open the +created design file `mkTop.v`. + +12. Copy the contents of the Verilog code `mkTop.v` as previously modified. + +13. From the *Sources* drop down, select *Constraints* and select the created +constraints file `new_constraints.xdc`. + +14. Copy and modify the constraints file according to the specific FPGA board +that aligns with the project. + +15. To run and compile the code, on the left-hand bar labeled *Flow Navigator*, +under *Project Manager* > *Program and Debug*, Select *Generate Bitstream* to +compile the code. + +16. Once bitsream generation is completed. Navigate under *Hardware Manager* > +*Open Target* > *Plug in your FPGA board via USB* > *Auto Connect* > *Program +Device*. + +The LEDs on your FPGA board should now display the LEDs counting up in +correspondence to its binary value. + +# Acknowledgements +[(Back to top)](#table-of-contents) + +This tutorial has been created by Sukhman Kahlon. + +Special thanks to the following individuals and teams for their guidance and +support throughout this project: + +Pavlo Vlastos – for providing clear and helpful instructions on setting up +Vivado. + +Ryan Scott and the Bluespec team – for their assistance with `copilot-bluespec` +and Bluespec. + +Ivan Perez – for his support with Copilot and set up for this example. + +Kaveh Zare - for sampling the tutorial and providing feedback. diff --git a/copilot/examples/fpga/HelloWorld/Top.bs b/copilot/examples/fpga/HelloWorld/Top.bs new file mode 100644 index 000000000..9e9f00c76 --- /dev/null +++ b/copilot/examples/fpga/HelloWorld/Top.bs @@ -0,0 +1,17 @@ +package Top where + + import HelloWorld + import HelloWorldIfc + import HelloWorldTypes + + helloWorldIfc :: Prelude.Module HelloWorldIfc + helloWorldIfc = + module + -- Register that connects the LEDs to values produced by Copilot + led_reg :: Prelude.Reg (Prelude.UInt 8) <- mkReg 0 + interface + led reg = action + led_reg := reg + + mkTop :: Prelude.Module Prelude.Empty + mkTop = mkHelloWorld helloWorldIfc diff --git a/copilot/examples/fpga/HelloWorld/constraints.xdc b/copilot/examples/fpga/HelloWorld/constraints.xdc new file mode 100644 index 000000000..119f897f3 --- /dev/null +++ b/copilot/examples/fpga/HelloWorld/constraints.xdc @@ -0,0 +1,17 @@ +set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports CLK] +create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK] + +set_property PACKAGE_PIN C2 [get_ports RST_N] +set_property IOSTANDARD LVCMOS33 [get_ports RST_N] + +# Switches +set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L12N_T1_MRCC_16 Sch=sw[0] +set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L13P_T2_MRCC_16 Sch=sw[1] +set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2] +set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3] + +# LEDs +set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { leds[0] }]; #IO_L24N_T3_35 Sch=led[4] +set_property -dict { PACKAGE_PIN J5 IOSTANDARD LVCMOS33 } [get_ports { leds[1] }]; #IO_25_35 Sch=led[5] +set_property -dict { PACKAGE_PIN T9 IOSTANDARD LVCMOS33 } [get_ports { leds[2] }]; #IO_L24P_T3_A01_D17_14 Sch=led[6] +set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { leds[3] }]; #IO_L24N_T3_A00_D16_14 Sch=led[7] diff --git a/copilot/examples/fpga/HelloWorld/mkTop.v b/copilot/examples/fpga/HelloWorld/mkTop.v new file mode 100644 index 000000000..23ecef6ab --- /dev/null +++ b/copilot/examples/fpga/HelloWorld/mkTop.v @@ -0,0 +1,75 @@ +// +// Generated by Bluespec Compiler, version 2025.01.1 (build 65e3a87a) +// +// Further modified by Sukhman Kahlon. +// +// On Thu Aug 14 14:52:29 PDT 2025 +// +// +// Ports: +// Name I/O size props +// CLK I 1 clock +// RST_N I 1 reset +// +// No combinational paths from inputs to outputs +// +// + +`ifdef BSV_ASSIGNMENT_DELAY +`else + `define BSV_ASSIGNMENT_DELAY +`endif + +`ifdef BSV_POSITIVE_RESET + `define BSV_RESET_VALUE 1'b1 + `define BSV_RESET_EDGE posedge +`else + `define BSV_RESET_VALUE 1'b0 + `define BSV_RESET_EDGE negedge +`endif + +module mkTop(CLK, + RST_N, + sw, + leds); + input CLK; + input RST_N; + input [3:0] sw; + output [3:0] leds; + + assign leds[3:0] = ifc_led_reg[3:0]; + + // register ifc_led_reg + reg [7 : 0] ifc_led_reg; + wire [7 : 0] ifc_led_reg$D_IN; + wire ifc_led_reg$EN; + + // register ifc_led_reg + assign ifc_led_reg$D_IN = sw[3:0]; + assign ifc_led_reg$EN = 1'd1 ; + + // handling of inlined registers + + always@(posedge CLK) + begin + if (RST_N == `BSV_RESET_VALUE) + begin + ifc_led_reg <= `BSV_ASSIGNMENT_DELAY 8'd0; + end + else + begin + if (ifc_led_reg$EN) + ifc_led_reg <= `BSV_ASSIGNMENT_DELAY ifc_led_reg$D_IN; + end + end + + // synopsys translate_off + `ifdef BSV_NO_INITIAL_BLOCKS + `else // not BSV_NO_INITIAL_BLOCKS + initial + begin + ifc_led_reg = 8'hAA; + end + `endif // BSV_NO_INITIAL_BLOCKS + // synopsys translate_on +endmodule // mkTop From 47f83a0d2744b0019437e3baf7e7a2f691808889 Mon Sep 17 00:00:00 2001 From: Sukhman Kahlon Date: Thu, 21 Aug 2025 16:19:06 -0700 Subject: [PATCH 216/483] copilot: Document changes in CHANGELOG. Refs #657. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index e92d2fe91..edb13dcd5 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-08-21 + * Add hello world example for FPGA. (#657) + 2025-07-07 * Version bump (4.5). (#642) From 8c588539f7522594b0b6872d53c47c875bc7a8ad Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 1 Sep 2025 03:47:43 +0000 Subject: [PATCH 217/483] copilot-visualizer: Relax version constraint on `ogma-extra`. Refs #645. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. The package `ogma-extra` has seen release 1.9, but copilot-visualizer depend on versions prior to 1.7. This commit relaxes the version constraint of on `ogma-extra` to allow installation of `copilot-visualizer` with versions in the 1.9 series. --- copilot-visualizer/copilot-visualizer.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index f0f59c93d..cf1399573 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -61,7 +61,7 @@ library filepath >= 1.4.2 && < 1.6, hint >= 0.9.0 && < 1.10, pretty >= 1.0 && < 1.2, - ogma-extra >= 1.6.0 && < 1.7, + ogma-extra >= 1.6.0 && < 1.10, text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, From 7f6042049506600a59edcb153cf532dee64fd154 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 1 Sep 2025 03:50:17 +0000 Subject: [PATCH 218/483] copilot-visualizer: Document changes in CHANGELOG. Refs #645. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 91c4b41fd..2b2449afa 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2025-08-31 + * Relax version constraint on `ogma-extra`. (#645) + 2025-07-07 * Version bump (4.5). (#642) * Include in mainline. (#624) From 70544fb89c82d09d66e0bfef256fec863cc70bb7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 10:21:46 +0000 Subject: [PATCH 219/483] copilot-theorem: Remove unused function Copilot.Theorem.Misc.SExpr.parseSExpr. Refs #661. The function `copilot-theorem:Copilot.Theorem.Misc.SExpr.parseSExpr` is not used anywhere in the implementation. The module is internal and not exposed to the user, so that function does not have and cannot have any users. This commit removes the function, as well as another definition in the module only used by `parseSExpr`, and an import only used by those two functions. --- .../src/Copilot/Theorem/Misc/SExpr.hs | 37 +------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs b/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs index f828850b2..cb7d35ff3 100644 --- a/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs +++ b/copilot-theorem/src/Copilot/Theorem/Misc/SExpr.hs @@ -2,10 +2,9 @@ {-# LANGUAGE Safe #-} -- | A representation for structured expression trees, with support for pretty --- printing and for parsing. +-- printing. module Copilot.Theorem.Misc.SExpr where -import Text.ParserCombinators.Parsec import Text.PrettyPrint.HughesPJ as PP hiding (char, Str) import Control.Monad @@ -70,37 +69,3 @@ toDoc shouldIndent printAtom expr = case expr of doc $$ indent (toDoc shouldIndent printAtom s) | otherwise = doc <+> toDoc shouldIndent printAtom s - --- | Parser for strings of characters separated by spaces into a structured --- tree. --- --- Parentheses are interpreted as grouping elements, that is, defining a --- 'List', which may be empty. -parser :: GenParser Char st (SExpr String) -parser = - choice [try unitP, nodeP, leafP] - - where - symbol = oneOf "!#$%&|*+-/:<=>?@^_~." - lonelyStr = many1 (alphaNum <|> symbol) - - unitP = string "()" >> return unit - - leafP = atom <$> lonelyStr - - nodeP = do void $ char '(' - spaces - st <- sepBy parser spaces - spaces - void $ char ')' - return $ List st - --- | Parser for strings of characters separated by spaces into a structured --- tree. --- --- Parentheses are interpreted as grouping elements, that is, defining a --- 'List', which may be empty. -parseSExpr :: String -> Maybe (SExpr String) -parseSExpr str = case parse parser "" str of - Left s -> error (show s) -- Nothing - Right t -> Just t From 2d601665aef9b79007e701c937139e8653248905 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 10:23:29 +0000 Subject: [PATCH 220/483] copilot-theorem: Removed unnecessary dependency on parsec. Refs #661. The function `copilot-theorem:Copilot.Theorem.Misc.SExpr.parseSExpr` is not used anywhere in the implementation. The module is internal and not exposed to the user, so that function does not have and cannot have any users. A prior commit has removed the function, which was the only user of the `parsec` library across `copilot-theorem`, thus rendering that dependency no longer necessary. This commit removes that dependency from the Cabal file. --- copilot-theorem/copilot-theorem.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 47cd076b0..f120240cb 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -54,7 +54,6 @@ library , libBF >= 0.6.2 && < 0.7 , mtl >= 2.0 && < 2.4 , panic >= 0.4.0 && < 0.5 - , parsec >= 2.0 && < 3.2 , parameterized-utils >= 2.1.1 && < 2.2 , pretty >= 1.0 && < 1.2 , process >= 1.6 && < 1.7 From b0d5e2bc652ff4cb3cd15e977ee49e503501dafa Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 10:27:31 +0000 Subject: [PATCH 221/483] copilot-theorem: Document changes in CHANGELOG. Refs #661. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index da078ea43..cd1fcbb93 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Remove unused function Copilot.Theorem.Misc.SExpr.parseSExpr. (#661) + 2025-07-07 * Version bump (4.5). (#642) * Removed unused pragmas. (#613) From 3614cc1cf37fceed88bf325352d0559bd31d96ce Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 222/483] copilot-core: Version bump (4.5.1). Refs #666. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index ffd4031a9..78e2d5808 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.5 +version: 4.5.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From 4af76e9b143c67b4f1a49b65eb8297a356c0ee6d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 223/483] copilot-bluespec: Version bump (4.5.1). Refs #666. --- copilot-bluespec/copilot-bluespec.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 90d9f89d1..f62cb2432 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.5 +version : 4.5.1 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -44,7 +44,7 @@ library , filepath >= 1.4 && < 1.6 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.5 && < 4.6 + , copilot-core >= 4.5.1 && < 4.6 , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From c4a80ceef2c5e2ca47ec44023ea2f9fdeb06d8ff Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 224/483] copilot-c99: Version bump (4.5.1). Refs #666. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 17ec0d950..b33f4e5bc 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.5 +version : 4.5.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.5 && < 4.6 + , copilot-core >= 4.5.1 && < 4.6 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From f13c9f567c4abfdca96284b7a473f0fc77bbd070 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 225/483] copilot-prettyprinter: Version bump (4.5.1). Refs #666. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 2953cfef6..a5acd909f 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.5 +version: 4.5.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.5 && < 4.6 + copilot-core >= 4.5.1 && < 4.6 exposed-modules: From 808c8c66529c5036dccf29b6c51200b101e7ddd7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 226/483] copilot-interpreter: Version bump (4.5.1). Refs #666. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 4bcfb3138..b6b2c4fe7 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.5 +version: 4.5.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.5 && < 4.6 + copilot-core >= 4.5.1 && < 4.6 exposed-modules: From 680dbd6119e28210b10dc25e5474522ed004033f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 227/483] copilot-theorem: Version bump (4.5.1). Refs #666. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index f120240cb..86bb1c68c 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.5 +version : 4.5.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -62,8 +62,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.5 && < 4.6 - , copilot-prettyprinter >= 4.5 && < 4.6 + , copilot-core >= 4.5.1 && < 4.6 + , copilot-prettyprinter >= 4.5.1 && < 4.6 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 67977320ff07b2d9be7fa1d636d7ead2c0230e69 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 228/483] copilot-language: Version bump (4.5.1). Refs #666. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index e9d4de9a9..7d02c0bfc 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.5 +version: 4.5.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.5 && < 4.6 - , copilot-interpreter >= 4.5 && < 4.6 - , copilot-theorem >= 4.5 && < 4.6 + , copilot-core >= 4.5.1 && < 4.6 + , copilot-interpreter >= 4.5.1 && < 4.6 + , copilot-theorem >= 4.5.1 && < 4.6 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From d1b0ffdf8e514113b57c4df4339db83a1c447613 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 229/483] copilot-libraries: Version bump (4.5.1). Refs #666. --- copilot-libraries/copilot-libraries.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 214fe7dc4..3402b5296 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.5 +version: 4.5.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -38,10 +38,10 @@ library build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.8 - , mtl >= 2.0 && < 2.4 - , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.5 && < 4.6 + , containers >= 0.4 && < 0.8 + , mtl >= 2.0 && < 2.4 + , parsec >= 2.0 && < 3.2 + , copilot-language >= 4.5.1 && < 4.6 exposed-modules: Copilot.Library.Libraries From eec16989d5f0110dec1750de83db1a521f5a4fb2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 230/483] copilot-verifier: Version bump (4.5.1). Refs #666. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 8b915e154..808ac3823 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.5 +Version: 4.5.1 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.5 && < 4.6, - copilot-core >= 4.5 && < 4.6, - copilot-theorem >= 4.5 && < 4.6, + copilot-c99 >= 4.5.1 && < 4.6, + copilot-core >= 4.5.1 && < 4.6, + copilot-theorem >= 4.5.1 && < 4.6, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.5 && < 4.6, - copilot-language >= 4.5 && < 4.6, - copilot-libraries >= 4.5 && < 4.6, - copilot-prettyprinter >= 4.5 && < 4.6, + copilot >= 4.5.1 && < 4.6, + copilot-language >= 4.5.1 && < 4.6, + copilot-libraries >= 4.5.1 && < 4.6, + copilot-prettyprinter >= 4.5.1 && < 4.6, copilot-verifier exposed-modules: Copilot.Verifier.Examples From d7c1281ae06f74b8b7d2140725d71126bb9a2dd3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 231/483] copilot-visualizer: Version bump (4.5.1). Refs #666. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index cf1399573..c88f26f1f 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.5 +version: 4.5.1 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.5 && < 4.6, - copilot-core >= 4.5 && < 4.6, - copilot-interpreter >= 4.5 && < 4.6, - copilot-language >= 4.5 && < 4.6 + copilot >= 4.5.1 && < 4.6, + copilot-core >= 4.5.1 && < 4.6, + copilot-interpreter >= 4.5.1 && < 4.6, + copilot-language >= 4.5.1 && < 4.6 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.5 && < 4.6, + copilot >= 4.5.1 && < 4.6, copilot-visualizer if flag(examples) From d1d2b270598cf398de857c3ae289e8b37f6549f9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:56:58 -0700 Subject: [PATCH 232/483] copilot: Version bump (4.5.1). Refs #666. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 6d9570b19..a15dd5cf3 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.5 +version: 4.5.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.5 && < 4.6 - , copilot-theorem >= 4.5 && < 4.6 - , copilot-language >= 4.5 && < 4.6 - , copilot-libraries >= 4.5 && < 4.6 - , copilot-c99 >= 4.5 && < 4.6 - , copilot-prettyprinter >= 4.5 && < 4.6 + , copilot-core >= 4.5.1 && < 4.6 + , copilot-theorem >= 4.5.1 && < 4.6 + , copilot-language >= 4.5.1 && < 4.6 + , copilot-libraries >= 4.5.1 && < 4.6 + , copilot-c99 >= 4.5.1 && < 4.6 + , copilot-prettyprinter >= 4.5.1 && < 4.6 exposed-modules: Language.Copilot, Language.Copilot.Main From 051b91322eec26cc5aae4efb401b4ca12cf81b70 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 233/483] copilot-core: Document changes in CHANGELOG. Refs #666. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 810d39b24..62e89d1ef 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) * Remove deprecated fields of Copilot.Core.Expr.UExpr. (#636) From fda59ae9fef63606bb655c6fd7b7ea9f8dacd688 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 234/483] copilot-bluespec: Document changes in CHANGELOG. Refs #666. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index ccfaad8ab..e7472493b 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) * Include `copilot-bluespec` in mainline `copilot` repo. (#623) From 7448c20bc13adc1a1d2aedc74a1cac24219ee8b3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 235/483] copilot-c99: Document changes in CHANGELOG. Refs #666. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 7b7d3aeb6..e5f0b606d 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) From fb15c4ee661b2c6da8745ee6a2ffc962b9cd93e7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 236/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #666. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 8f94d9fa6..73ca7c5bf 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) From 65c53b3ea01a33e60b803fecabc9399adb2bda45 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 237/483] copilot-interpreter: Document changes in CHANGELOG. Refs #666. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index cd1e6b431..e819c4162 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) From 4406c1844bd33e32864efaea71473459c1e6a6e7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 238/483] copilot-theorem: Document changes in CHANGELOG. Refs #666. --- copilot-theorem/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index cd1fcbb93..621417e73 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ 2025-09-07 + * Version bump (4.5.1). (#666) * Remove unused function Copilot.Theorem.Misc.SExpr.parseSExpr. (#661) 2025-07-07 From 6034ccfd294454190470fa96cb46ca46f5598b92 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 239/483] copilot-language: Document changes in CHANGELOG. Refs #666. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 60112d6f8..ed8ebbebb 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) From bd1c2ce43b15e68bf1b086cb2f40fbb9edb9d32b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 240/483] copilot-libraries: Document changes in CHANGELOG. Refs #666. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index c724f29b5..5d4323d7f 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) From fe74b7b0686eef85bbf68f96c48bb3924cc74987 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 241/483] copilot-verifier: Document changes in CHANGELOG. Refs #666. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 59ac57d9a..e8b89e276 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2025-09-07 + * Version bump (4.5.1). (#666) + 2025-07-07 * Version bump (4.5). (#642) * Include `copilot-verifier` in mainline `copilot` repo. (#622) From e8a1af051cf43545dfc35302a27dc3032a38fc96 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 242/483] copilot-visualizer: Document changes in CHANGELOG. Refs #666. --- copilot-visualizer/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 2b2449afa..cf9f151fb 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,4 +1,5 @@ -2025-08-31 +2025-09-07 + * Version bump (4.5.1). (#666) * Relax version constraint on `ogma-extra`. (#645) 2025-07-07 From 4615b1a78b36768d6c271ddea5e399a56b342243 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 7 Sep 2025 14:58:31 -0700 Subject: [PATCH 243/483] copilot: Document changes in CHANGELOG. Refs #666. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index edb13dcd5..7b69ed049 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ -2025-08-21 +2025-09-07 + * Version bump (4.5.1). (#666) * Add hello world example for FPGA. (#657) 2025-07-07 From da44685dc2bbecd5f363ddf89b6b175385897474 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 3 Nov 2025 06:28:53 -0500 Subject: [PATCH 244/483] copilot-bluespec: Flip direction of interface inputs and outputs in Bluespec. Refs #677. The current Bluespec backend leads to Verilog code that requires manual manipulations in order to work correctly. Specifically, Copilot externs, which are inputs to the Copilot monitoring system, are treated as _outputs_ in Verilog, and Copilot triggers, which can be considered outputs of the monitoring system, are treated as _inputs_. This order of inputs and outputs is the opposite what we would like users to work with, and of what other backends generate (e.g., C99). This commit updates the internals of `copilot-bluespec` to flip the order in which generated Bluespec module interfaces declare their inputs and outputs. A module interface now represents each Copilot extern as a method of type ` -> Action` (where `` is the type of the extern), as this type directly translates to a Verilog input. Moreover, a module interface now represents trigger arguments as interface methods of type `_i`, where each `_i` is the type of each trigger argument. Each `_i` directly translates to a Verilog output. In addition to changing the module interface definitions, this commit also changes the generated modules that instantiate the new interfaces. One of the more notable changes is that all extern values are stored as a Bluespec `Wire` in the module internals, as using `Wire`s leads to compact Verilog code without adding any user-visible inputs or outputs. Every time a value is read from an extern, the corresponding `Wire` must be read from, which means that several parts of the module internals have been updated to reference these `Wire`s. --- .../src/Copilot/Compile/Bluespec/CodeGen.hs | 155 +++++++++++++++--- .../src/Copilot/Compile/Bluespec/Expr.hs | 5 +- .../src/Copilot/Compile/Bluespec/Name.hs | 21 ++- 3 files changed, 149 insertions(+), 32 deletions(-) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs index 6d9fde04d..859c73dec 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/CodeGen.hs @@ -14,12 +14,17 @@ module Copilot.Compile.Bluespec.CodeGen -- * Stream generators , mkGenFun + -- * External streams + , mkExtWireDecln + -- * Monitor processing , mkStepRule + , mkExtRule , mkTriggerRule -- * Module interface specifications , mkSpecIfcFields + , mkSpecIfcRulesFields ) where -- External imports @@ -48,7 +53,17 @@ mkGenFun name expr ty = [] where nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name - def = BS.CClause [] [] (transExpr expr) + def = BS.CClause [] [] (transExpr expr) + +-- | Bind a @Wire@ variable using @mkBypassWire@. +mkExtWireDecln :: String -> Type a -> BS.CStmt +mkExtWireDecln name ty = + BS.CSBindT + (BS.CPVar (BS.mkId BS.NoPos (fromString (wireName name)))) + Nothing + [] + (BS.CQType [] (tWire `BS.TAp` transType ty)) + (BS.CVar (BS.mkId BS.NoPos "mkBypassWire")) -- | Bind a buffer variable and initialise it with the stream buffer. mkBuffDecln :: forall a. Id -> Type a -> [a] -> [BS.CStmt] @@ -133,44 +148,124 @@ mkAccessDecln sId ty xs = -- | Define fields for a module interface containing a specification's trigger -- functions and external variables. -mkSpecIfcFields :: [Trigger] -> [External] -> [BS.CField] -mkSpecIfcFields triggers exts = +mkSpecIfcFields :: [UniqueTrigger] -> [External] -> [BS.CField] +mkSpecIfcFields uniqueTriggers exts = + concatMap mkTriggerFields uniqueTriggers ++ map mkExtField exts + where + -- trigger_guard :: Bool + -- trigger_arg0 :: arg_ty_0 + -- ... + -- trigger_arg(n-1) :: arg_ty_(n-1) + mkTriggerFields :: UniqueTrigger -> [BS.CField] + mkTriggerFields (UniqueTrigger uniqueName (Trigger _name _ args)) = + triggerGuardField : triggerArgFields + where + triggerGuardField :: BS.CField + triggerGuardField = mkField (guardName uniqueName) [] BS.tBool + + triggerArgFields :: [BS.CField] + triggerArgFields = + zipWith + (\(UExpr arg _) argName -> mkField argName [] (transType arg)) + args + (argNames uniqueName) + + -- ext :: ty -> Action + mkExtField :: External -> BS.CField + mkExtField (External name ty) = + mkField + name + [ BS.PIPrefixStr "" + , BS.PIArgNames [BS.mkId BS.NoPos $ fromString $ lowercaseName name] + ] + (BS.tArrow `BS.TAp` transType ty `BS.TAp` BS.tAction) + +-- | Define fields for a module interface containing the actions to perform for +-- a specification's trigger functions and external variables. +mkSpecIfcRulesFields :: [Trigger] -> [External] -> [BS.CField] +mkSpecIfcRulesFields triggers exts = map mkTriggerField triggers ++ map mkExtField exts where - -- trigger :: args_1 -> ... -> args_n -> Action + -- trigger_action :: arg_ty_0 -> ... -> arg_ty_(n-1) -> Action mkTriggerField :: Trigger -> BS.CField mkTriggerField (Trigger name _ args) = - mkField name $ - foldr - (\(UExpr arg _) res -> BS.tArrow `BS.TAp` transType arg `BS.TAp` res) - BS.tAction - args + mkField (actionName name) [] triggerFieldType + where + triggerFieldType :: BS.CType + triggerFieldType = foldr addArgType BS.tAction args - -- ext :: Reg ty + addArgType :: UExpr -> BS.CType -> BS.CType + addArgType (UExpr arg _) res = + BS.tArrow `BS.TAp` transType arg `BS.TAp` res + + -- ext_action :: ActionValue ty mkExtField :: External -> BS.CField mkExtField (External name ty) = - mkField name $ tReg `BS.TAp` transType ty + mkField (actionName name) [] (BS.tActionValue `BS.TAp` transType ty) + +-- | Define a rule for an external stream that performs an action on the most +-- recently computed value from the stream. +mkExtRule :: External -> BS.CRule +mkExtRule (External name _) = + -- rules + -- "ext": when True ==> + -- action + -- extVal <- ifcRules.ext_action + -- ifc.ext extVal + BS.CRule + [] + (Just $ cLit $ BS.LString name) + [BS.CQFilter $ BS.CCon BS.idTrue []] + (BS.Caction BS.NoPos [callExtAction, callExt]) + where + ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + ifcRulesArgId = BS.mkId BS.NoPos $ fromString ifcRulesArgName + + extActionId = BS.mkId BS.NoPos $ fromString $ actionName name + extId = BS.mkId BS.NoPos $ fromString name + extValId = BS.mkId BS.NoPos $ fromString $ name ++ "Val" --- | Define a rule for a trigger function. + -- extVal <- ifcRules.ext_action + callExtAction :: BS.CStmt + callExtAction = + BS.CSBind + (BS.CPVar extValId) + Nothing + [] + (BS.CSelect (BS.CVar ifcRulesArgId) extActionId) + + -- ifc.ext extVal + callExt :: BS.CStmt + callExt = + BS.CSExpr Nothing $ + BS.CApply (BS.CSelect (BS.CVar ifcArgId) extId) [BS.CVar extValId] + +-- | Define a rule for a trigger function that performs an action when the rule +-- fires. mkTriggerRule :: UniqueTrigger -> BS.CRule mkTriggerRule (UniqueTrigger uniqueName (Trigger name _ args)) = + -- rules + -- "trigger": when ifc.trigger_guard ==> + -- ifcRules.trigger_action ifc.trigger_arg0 BS.CRule [] (Just $ cLit $ BS.LString uniqueName) [ BS.CQFilter $ - BS.CVar $ BS.mkId BS.NoPos $ - fromString $ guardName uniqueName + BS.CSelect + (BS.CVar ifcArgId) + (BS.mkId BS.NoPos $ fromString $ guardName uniqueName) ] - (BS.CApply nameExpr args') + (BS.CApply actionNameExpr args') where - ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + ifcRulesArgId = BS.mkId BS.NoPos $ fromString ifcRulesArgName -- Note that we use 'name' here instead of 'uniqueName', as 'name' is the -- name of the actual external function. - nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name - nameExpr = BS.CSelect (BS.CVar ifcArgId) nameId + actionNameId = BS.mkId BS.NoPos $ fromString $ actionName name + actionNameExpr = BS.CSelect (BS.CVar ifcRulesArgId) actionNameId args' = take (length args) (map argCall (argNames uniqueName)) - argCall = BS.CVar . BS.mkId BS.NoPos . fromString + argCall = BS.CSelect (BS.CVar ifcArgId) . BS.mkId BS.NoPos . fromString -- | Writes the @step@ rule that updates all streams. mkStepRule :: [Stream] -> Maybe BS.CRule @@ -233,19 +328,20 @@ mkStructDecln x = -- Derive a Bits instance so that we can put this struct in a Reg [BS.CTypeclass BS.idBits] where - structId = BS.mkId BS.NoPos $ fromString $ uppercaseName $ typeName x + structId = BS.mkId BS.NoPos $ fromString $ uppercaseName $ typeName x structFields = map mkStructField $ toValues x mkStructField :: Value a -> BS.CField mkStructField (Value ty field) = - mkField (fieldName field) (transType ty) + mkField (fieldName field) [] (transType ty) --- | Write a field of a struct or interface, along with its type. -mkField :: String -> BS.CType -> BS.CField -mkField name ty = +-- | Write a field of a struct or interface, along with its pragmas and type +-- signature. +mkField :: String -> [BS.IfcPragma] -> BS.CType -> BS.CField +mkField name pragmas ty = BS.CField { BS.cf_name = BS.mkId BS.NoPos $ fromString $ lowercaseName name - , BS.cf_pragmas = Nothing + , BS.cf_pragmas = Just pragmas , BS.cf_type = BS.CQType [] ty , BS.cf_default = [] , BS.cf_orig_type = Nothing @@ -260,3 +356,12 @@ tReg = BS.TCon $ , BS.tcon_sort = BS.TIstruct (BS.SInterface []) [BS.id_write BS.NoPos, BS.id_read BS.NoPos] } + +-- | The @Wire@ Bluespec type. +tWire :: BS.CType +tWire = BS.TCon $ + BS.TyCon + { BS.tcon_name = BS.mkId BS.NoPos "Wire" + , BS.tcon_kind = Just (BS.Kfun BS.KStar BS.KStar) + , BS.tcon_sort = BS.TItype 0 tReg + } diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs index c4978aef5..7297fad2f 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs @@ -51,11 +51,8 @@ transExpr (Drop _ amount sid) = [BS.CLit $ BS.CLiteral BS.NoPos index] transExpr (ExternVar _ name _) = - let ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName in BS.CSelect - (BS.CSelect - (BS.CVar ifcArgId) - (BS.mkId BS.NoPos $ fromString $ lowercaseName name)) + (BS.CVar $ BS.mkId BS.NoPos $ fromString $ wireName name) (BS.id_read BS.NoPos) transExpr (Label _ _ e) = transExpr e -- ignore label diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs index dbc714a3b..19b78ca90 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs @@ -1,9 +1,11 @@ -- | Naming of variables and functions in Bluespec. module Copilot.Compile.Bluespec.Name - ( argNames + ( actionName + , argNames , generatorName , guardName , ifcArgName + , ifcRulesArgName , indexName , lowercaseName , specIfcName @@ -13,6 +15,7 @@ module Copilot.Compile.Bluespec.Name , streamElemName , streamName , uppercaseName + , wireName ) where -- External imports @@ -41,11 +44,14 @@ specTypesPkgName prefix = prefix ++ "Types" streamElemName :: Id -> Int -> String streamElemName sId n = streamName sId ++ "_" ++ show n --- | The name of the variable of type @Ifc@. This is used to select --- trigger functions and external variables. +-- | The name of a variable of type @Ifc@. ifcArgName :: String ifcArgName = "ifc" +-- | The name of a variable of type @RulesIfc@. +ifcRulesArgName :: String +ifcRulesArgName = "ifcRules" + -- | Create a Bluespec name that must start with an uppercase letter (e.g., a -- struct or interface name). If the supplied name already begins with an -- uppercase letter, this function returns the name unchanged. Otherwise, this @@ -86,6 +92,15 @@ generatorName sId = streamName sId ++ "_gen" guardName :: String -> String guardName name = lowercaseName name ++ "_guard" +-- | Turn the name of a trigger of external stream into the name of a method +-- that performs a Bluespec @Action@. +actionName :: String -> String +actionName name = lowercaseName name ++ "_action" + +-- | Turn the name of an external stream into a Bluespec @Wire@. +wireName :: String -> String +wireName name = lowercaseName name ++ "_wire" + -- | Turn a trigger name into a an trigger argument name. argName :: String -> Int -> String argName name n = lowercaseName name ++ "_arg" ++ show n From c3543283a9af6720dc86067999042f30d0fbd33d Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 3 Nov 2025 06:29:25 -0500 Subject: [PATCH 245/483] copilot-bluespec: Update Copilot.Compile.Bluespec.Compile to reflect new input/output order. Refs #677. The current Bluespec backend leads to Verilog code that requires manual manipulations in order to work correctly. Specifically, Copilot externs, which are inputs to the Copilot monitoring system, are treated as _outputs_ in Verilog, and Copilot triggers, which can be considered outputs of the monitoring system, are treated as _inputs_. This order of inputs and outputs is the opposite what we would like users to work with, and of what other backends generate (e.g., C99). A prior commit has updated the internals of `copilot-bluespec` to invert the order in which Verilog inputs and outputs are declared. This commit adapts the top-level code in `Copilot.Compile.Bluespec.Compile` accordingly. In particular, the `compileWith` function now generates a `.bs` file that defines two module interfaces: one interface that contains the core methods that will comprise the generated Verilog code, and another interface that helps automate the process of defining a `Module Empty` value that is more suitable for software simulation purposes. Previously, `compileWith` generated a separate `Ifc.bs` file that contained nothing but the module interface in isolation so that it could be used separately from the interface's `Module` definition. This distinction no longer makes as much sense, as in the new design, as (1) there are now multiple interfaces, and (2) it is unlikely that the interfaces are not particularly useful without having the `Module` definitions to accompany them. As such, this commit now omits generating a `Ifc.bs` file, instead opting to generate the interfaces as part of `.bs`. --- .../src/Copilot/Compile/Bluespec/Compile.hs | 293 ++++++++++++------ .../src/Copilot/Compile/Bluespec/Name.hs | 5 + 2 files changed, 195 insertions(+), 103 deletions(-) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs index c4701eaf7..5343fd6a1 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Compile.hs @@ -57,13 +57,11 @@ compileWith bsSettings prefix spec | otherwise = do let typesBsFile = render $ pPrint $ compileTypesBS bsSettings prefix spec - ifcBsFile = render $ pPrint $ compileIfcBS bsSettings prefix spec bsFile = render $ pPrint $ compileBS bsSettings prefix spec let dir = bluespecSettingsOutputDirectory bsSettings createDirectoryIfMissing True dir writeFile (dir specTypesPkgName prefix ++ ".bs") typesBsFile - writeFile (dir specIfcPkgName prefix ++ ".bs") ifcBsFile writeFile (dir "bs_fp.c") copilotBluespecFloatingPointC writeFile (dir "BluespecFP.bsv") copilotBluespecFloatingPointBSV writeFile (dir prefix ++ ".bs") bsFile @@ -93,32 +91,8 @@ compile :: String -> Spec -> IO () compile = compileWith mkDefaultBluespecSettings -- | Generate a @.bs@ file from a 'Spec'. This is the main payload of --- the Bluespec backend. --- --- The generated Bluespec file will import a handful of files from the standard --- library, as well as the following generated files: --- --- * @Ifc.bs@, which defines the interface containing the trigger --- functions and external variables. --- --- * @Types.bs@, which defines any structs used in the 'Spec'. --- --- It will also generate a @mk :: Module Ifc -> Module Empty@ --- function, which defines the module structure for this 'Spec'. The --- @mk@ function has the following structure: --- --- * First, bind the argument of type @Module Ifc@ so that trigger --- functions can be invoked and external variables can be used. --- --- * Next, declare stream buffers and indices. --- --- * Next, declare generator functions for streams, accessor functions for --- streams, and guard functions for triggers. --- --- * Next, declare rules for each trigger function. --- --- * Finally, declare a single rule that updates the stream buffers and --- indices. +-- the Bluespec backend. See the @copilot-bluespec/DESIGN.md@ document for a +-- high-level description of what this file contains. compileBS :: BluespecSettings -> String -> Spec -> BS.CPackage compileBS _bsSettings prefix spec = BS.CPackage @@ -126,53 +100,157 @@ compileBS _bsSettings prefix spec = (Right []) (stdLibImports ++ genImports) [] - [moduleDef] + [ ifcDef + , mkModuleDefPragma + , mkModuleDef + , ifcRulesDef + , mkModuleRulesDef + , addModuleRulesDef + ] [] where -- import Types - -- import Ifc genImports :: [BS.CImport] genImports = [ BS.CImpId False $ BS.mkId BS.NoPos $ fromString $ specTypesPkgName prefix - , BS.CImpId False $ BS.mkId BS.NoPos $ fromString - $ specIfcPkgName prefix , BS.CImpId False $ BS.mkId BS.NoPos "BluespecFP" ] - moduleDef :: BS.CDefn - moduleDef = BS.CValueSign $ - BS.CDef - (BS.mkId BS.NoPos $ fromString $ "mk" ++ prefix) - -- :: Module Ifc -> Module Empty - (BS.CQType - [] - (BS.tArrow - `BS.TAp` (BS.tModule `BS.TAp` ifcTy) - `BS.TAp` (BS.tModule `BS.TAp` emptyTy))) - [ BS.CClause [BS.CPVar ifcModId] [] $ - BS.Cmodule BS.NoPos $ - BS.CMStmt - (BS.CSBind (BS.CPVar ifcArgId) Nothing [] (BS.CVar ifcModId)) - : map BS.CMStmt mkGlobals ++ - [ BS.CMStmt $ BS.CSletrec genFuns - , BS.CMrules $ BS.Crules [] rules - ] - ] + -- interface Ifc {-# always_ready, always_enabled #-} = + -- ... + ifcDef :: BS.CDefn + ifcDef = BS.Cstruct + True + (BS.SInterface [BS.PIAlwaysRdy, BS.PIAlwaysEnabled]) + (BS.IdK ifcId) + [] -- No type variables + ifcFields + [] -- No derived instances - ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName - ifcModId = BS.mkId BS.NoPos "ifcMod" + -- {-# properties mkFibs = { verilog } #-} + mkModuleDefPragma :: BS.CDefn + mkModuleDefPragma = BS.CPragma $ BS.Pproperties mkModuleDefId [BS.PPverilog] - rules :: [BS.CRule] - rules = map mkTriggerRule uniqueTriggers ++ maybeToList (mkStepRule streams) + -- mk :: Module Ifc + -- mk = + -- module + -- ... + mkModuleDef :: BS.CDefn + mkModuleDef = BS.CValueSign $ + BS.CDef + mkModuleDefId + (BS.CQType [] (BS.tModule `BS.TAp` ifcTy)) + [ BS.CClause [] [] $ + BS.Cmodule BS.NoPos $ + wireGlobalStmts ++ genFunStmts ++ ruleIfcStmts + ] + where + wireGlobalStmts :: [BS.CMStmt] + wireGlobalStmts = map BS.CMStmt (mkExtWires ++ mkGlobals) + + genFunStmts :: [BS.CMStmt] + genFunStmts = + -- language-bluespec's pretty-printer will error if it encounters a + -- CSletrec with an empty list of definitions, so avoid generating a + -- CSletrec if there are no streams. + [ BS.CMStmt $ BS.CSletrec genFuns | not (null genFuns) ] + + ruleIfcStmts :: [BS.CMStmt] + ruleIfcStmts = + [ BS.CMrules $ BS.Crules [] $ maybeToList $ mkStepRule streams + , BS.CMinterface $ BS.Cinterface BS.NoPos (Just ifcId) ifcMethodImpls + ] + + -- interface RulesIfc = + -- ... + ifcRulesDef :: BS.CDefn + ifcRulesDef = + BS.Cstruct + True + (BS.SInterface []) + (BS.IdK ifcRulesId) + [] -- No type variables + ifcRulesFields + [] -- No derived instances + + -- mkRules :: Ifc -> RulesIfc -> Rules + -- mkRules ifc ifcRules = + -- rules + -- ... + mkModuleRulesDef :: BS.CDefn + mkModuleRulesDef = + BS.CValueSign $ + BS.CDef + mkModuleRulesDefId + (BS.CQType [] mkModuleRulesType) + [ BS.CClause + (map BS.CPVar [ifcArgId, ifcRulesArgId]) + [] + (BS.Crules [] moduleRules) + ] + where + -- Ifc -> RulesIfc -> Rules + mkModuleRulesType :: BS.CType + mkModuleRulesType = + BS.tArrow `BS.TAp` ifcTy `BS.TAp` + (BS.tArrow `BS.TAp` ifcRulesTy `BS.TAp` BS.tRules) + + -- rules + -- ... + moduleRules :: [BS.CRule] + moduleRules = map mkTriggerRule uniqueTriggers ++ map mkExtRule exts + + -- addRules :: Ifc -> RulesIfc -> Module Empty + -- addRules ifc ifcRules = addRules (mkRules ifc ifcRules) + addModuleRulesDef :: BS.CDefn + addModuleRulesDef = + BS.CValueSign $ + BS.CDef + addModuleRulesDefId + (BS.CQType [] addModuleRulesType) + [ BS.CClause + (map BS.CPVar [ifcArgId, ifcRulesArgId]) + [] + addModuleRulesExpr + ] + where + -- Ifc -> RulesIfc -> Module Empty + addModuleRulesType :: BS.CType + addModuleRulesType = + BS.tArrow `BS.TAp` ifcTy `BS.TAp` + (BS.tArrow `BS.TAp` ifcRulesTy `BS.TAp` + (BS.tModule `BS.TAp` emptyTy)) + + -- addRules (mkRules ifc ifcRules) + addModuleRulesExpr :: BS.CExpr + addModuleRulesExpr = + BS.CApply + (BS.CVar (BS.idAddRules BS.NoPos)) + [BS.CApply + (BS.CVar mkModuleRulesDefId) + (map BS.CVar [ifcArgId, ifcRulesArgId])] + + mkModuleDefId = + BS.mkId BS.NoPos $ fromString $ "mk" ++ prefix + mkModuleRulesDefId = + BS.mkId BS.NoPos $ fromString $ "mk" ++ prefix ++ "Rules" + addModuleRulesDefId = + BS.mkId BS.NoPos $ fromString $ "add" ++ prefix ++ "Rules" streams = specStreams spec triggers = specTriggers spec uniqueTriggers = mkUniqueTriggers triggers exts = gatherExts streams triggers + -- Remove duplicates due to multiple guards for the same trigger. + triggersNoDups = nubBy compareTrigger triggers + + ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName + ifcRulesArgId = BS.mkId BS.NoPos $ fromString ifcRulesArgName + ifcId = BS.mkId BS.NoPos $ fromString $ specIfcName prefix - ifcFields = mkSpecIfcFields triggers exts + ifcFields = mkSpecIfcFields uniqueTriggers exts ifcTy = BS.TCon (BS.TyCon { BS.tcon_name = ifcId , BS.tcon_kind = Just BS.KStar @@ -181,12 +259,31 @@ compileBS _bsSettings prefix spec = (map BS.cf_name ifcFields) }) + ifcRulesId = BS.mkId BS.NoPos $ fromString $ specIfcRulesName prefix + ifcRulesFields = mkSpecIfcRulesFields triggersNoDups exts + + ifcRulesTy = + BS.TCon $ + BS.TyCon + { BS.tcon_name = ifcRulesId + , BS.tcon_kind = Just BS.KStar + , BS.tcon_sort = + BS.TIstruct (BS.SInterface []) (map BS.cf_name ifcRulesFields) + } + emptyTy = BS.TCon (BS.TyCon { BS.tcon_name = BS.idEmpty , BS.tcon_kind = Just BS.KStar , BS.tcon_sort = BS.TIstruct (BS.SInterface []) [] }) + -- Bind @Wire@ variables for each extern stream. + mkExtWires :: [BS.CStmt] + mkExtWires = map extWireStmt exts + where + extWireStmt :: External -> BS.CStmt + extWireStmt (External name ty) = mkExtWireDecln name ty + -- Make buffer and index declarations for streams. mkGlobals :: [BS.CStmt] mkGlobals = concatMap buffDecln streams ++ map indexDecln streams @@ -194,11 +291,9 @@ compileBS _bsSettings prefix spec = buffDecln (Stream sId buff _ ty) = mkBuffDecln sId ty buff indexDecln (Stream sId _ _ _ ) = mkIndexDecln sId - -- Make generator functions, including trigger arguments. + -- Make generator functions for streams. genFuns :: [BS.CDefl] - genFuns = map accessDecln streams - ++ map streamGen streams - ++ concatMap triggerGen uniqueTriggers + genFuns = map accessDecln streams ++ map streamGen streams where accessDecln :: Stream -> BS.CDefl accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff @@ -206,56 +301,48 @@ compileBS _bsSettings prefix spec = streamGen :: Stream -> BS.CDefl streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty - triggerGen :: UniqueTrigger -> [BS.CDefl] - triggerGen (UniqueTrigger uniqueName (Trigger _name guard args)) = - guardDef : argDefs + -- Make interface methods for @Ifc@. + ifcMethodImpls :: [BS.CDefl] + ifcMethodImpls = + concatMap triggerMethodImpls uniqueTriggers + ++ map extMethodImpl exts + where + -- interface + -- ext val = ext_wire := val + extMethodImpl :: External -> BS.CDefl + extMethodImpl (External name _) = + BS.CLValue extMethodId [extMethodClause] [] + where + extMethodId = BS.mkId BS.NoPos (fromString name) + valId = BS.mkId BS.NoPos "val" + + -- ext val = ext_wire := val + extMethodClause :: BS.CClause + extMethodClause = + BS.CClause + [BS.CPVar valId] + [] + (BS.Cwrite + BS.NoPos + (BS.CVar (BS.mkId BS.NoPos (fromString (wireName name)))) + (BS.CVar valId)) + + -- interface + -- trig_guard = ... + -- trig_arg0 = ... + -- ... + -- trig_arg(n-1) = ... + triggerMethodImpls :: UniqueTrigger -> [BS.CDefl] + triggerMethodImpls uniqueTrigger = guardDef : argDefs where + UniqueTrigger uniqueName (Trigger _name guard args) = uniqueTrigger + guardDef = mkGenFun (guardName uniqueName) guard Bool argDefs = map argGen (zip (argNames uniqueName) args) argGen :: (String, UExpr) -> BS.CDefl argGen (argName, UExpr ty expr) = mkGenFun argName expr ty --- | Generate a @Ifc.bs@ file from a 'Spec'. This contains the --- definition of the @Ifc@ interface, which declares the types of all --- trigger functions and external variables. This is put in a separate file so --- that larger applications can use it separately. -compileIfcBS :: BluespecSettings -> String -> Spec -> BS.CPackage -compileIfcBS _bsSettings prefix spec = - BS.CPackage - ifcPkgId - (Right []) - (stdLibImports ++ genImports) - [] - [ifcDef] - [] - where - -- import Types - genImports :: [BS.CImport] - genImports = - [ BS.CImpId False $ BS.mkId BS.NoPos $ fromString - $ specTypesPkgName prefix - ] - - ifcId = BS.mkId BS.NoPos $ fromString $ specIfcName prefix - ifcPkgId = BS.mkId BS.NoPos $ fromString $ specIfcPkgName prefix - ifcFields = mkSpecIfcFields triggers exts - - streams = specStreams spec - exts = gatherExts streams triggers - - -- Remove duplicates due to multiple guards for the same trigger. - triggers = nubBy compareTrigger (specTriggers spec) - - ifcDef :: BS.CDefn - ifcDef = BS.Cstruct - True - (BS.SInterface []) - (BS.IdK ifcId) - [] -- No type variables - ifcFields - [] -- No derived instances - -- | Generate a @Types.bs@ file from a 'Spec'. This declares the types -- of any structs used by the Copilot specification. This is put in a separate -- file so that larger applications can more easily substitute their own struct diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs index 19b78ca90..ebb87f298 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Name.hs @@ -10,6 +10,7 @@ module Copilot.Compile.Bluespec.Name , lowercaseName , specIfcName , specIfcPkgName + , specIfcRulesName , specTypesPkgName , streamAccessorName , streamElemName @@ -35,6 +36,10 @@ specIfcName prefix = uppercaseName (specIfcPkgName prefix) specIfcPkgName :: String -> String specIfcPkgName prefix = prefix ++ "Ifc" +-- | Turn a specification name into the name of its rules-specific interface. +specIfcRulesName :: String -> String +specIfcRulesName prefix = uppercaseName (prefix ++ "RulesIfc") + -- | Turn a specification name into the name of the package that declares its -- struct types. specTypesPkgName :: String -> String From e5536e49a2ebb9e7377b912932eb7662d668ec8e Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 3 Nov 2025 06:30:12 -0500 Subject: [PATCH 246/483] copilot-bluespec: Update test suite to match new input/output order. Refs #677. The current Bluespec backend leads to Verilog code that requires manual manipulations in order to work correctly. Specifically, Copilot externs, which are inputs to the Copilot monitoring system, are treated as _outputs_ in Verilog, and Copilot triggers, which can be considered outputs of the monitoring system, are treated as _inputs_. This order of inputs and outputs is the opposite what we would like users to work with, and of what other backends generate (e.g., C99). A prior commit has updated the internals of `copilot-bluespec` to invert the order in which Verilog inputs and outputs are declared. This commit updates the test suite to work with the new design. In addition to changing the code to reflect the new order of inputs and outputs, this commit also updates the names of some internal variables from "`input`" to "`input1`". This change is necessary because "`input`" is a reserved keyword in Verilog, and giving a Bluespec interface method the same name as a reserved Verilog keyword becomes an error when the interface is used in the result type of a synthesized module. --- .../tests/Test/Copilot/Compile/Bluespec.hs | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs index ee99c6e75..c6319fafa 100644 --- a/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs +++ b/copilot-bluespec/tests/Test/Copilot/Compile/Bluespec.hs @@ -157,18 +157,15 @@ testRun = once $ ioProperty $ do [ "package Top where" , "" , "import CopilotTest" - , "import CopilotTestIfc" , "import CopilotTestTypes" , "" - , "copilotTestIfc :: Module CopilotTestIfc" - , "copilotTestIfc =" - , " module" - , " interface" - , " nop :: Action" - , " nop = return ()" - , "" , "mkTop :: Module Empty" - , "mkTop = mkCopilotTest copilotTestIfc" + , "mkTop =" + , " module" + , " copilotTestMod <- mkCopilotTest" + , " addCopilotTestRules copilotTestMod $" + , " interface CopilotTestRulesIfc" + , " nop_action = return ()" ] writeFile "Top.bs" bluespecProgram @@ -293,7 +290,7 @@ mkRegressionTest1 op haskellFun vals = t1 = typeOf t2 = typeOf - varName = "input" + varName = "input1" -- | Test the behavior of a binary operation (an @'Op2' a b c@ value) against -- its expected behavior (as a Haskell function of type @[a] -> [b] -> [c]@) @@ -762,7 +759,7 @@ mkTestCase1 genO gen = do t1 = typeOf t2 = typeOf - varName = "input" + varName = "input1" -- | Generate test cases for expressions that behave like binary functions. mkTestCase2 :: (Typed a, Typed b, Typed c) @@ -1016,24 +1013,25 @@ testRunCompareArgBluespecProgram proxy inputs outputType = unlines $ , "import Vector" , "" , "import CopilotTest" - , "import CopilotTestIfc" , "import CopilotTestTypes" , "" ] ++ inputVecDecls ++ [ "" - , "copilotTestIfc :: Module CopilotTestIfc" - , "copilotTestIfc =" + , "mkTop :: Module Empty" + , "mkTop =" , " module" + , " copilotTestMod <- mkCopilotTest" ] ++ inputRegs ++ [ " i :: Reg (Bit 64) <- mkReg 0" , " ready :: Reg Bool <- mkReg False" - , " interface" - , " printBack :: " ++ outputType ++ " -> Action" - , " printBack output = $display " ++ printBackDisplayArgs - , " when ready" , "" + , " addCopilotTestRules copilotTestMod $" + , " interface CopilotTestRulesIfc" + , " printBack_action :: " ++ outputType ++ " -> Action" + , " printBack_action output = $display " ++ printBackDisplayArgs + , " when ready" ] ++ inputMethods ++ [ "" @@ -1043,9 +1041,6 @@ testRunCompareArgBluespecProgram proxy inputs outputType = unlines $ ++ inputUpdates ++ [ " i := i + 1" , " ready := True" - , "" - , "mkTop :: Module Empty" - , "mkTop = mkCopilotTest copilotTestIfc" ] where printBackDisplayArgs :: String @@ -1070,11 +1065,9 @@ testRunCompareArgBluespecProgram proxy inputs outputType = unlines $ inputMethods :: [String] inputMethods = - concatMap - (\(bluespecType, varName, regName, _inputVecName, _inputVals) -> - [ " " ++ varName ++ " :: Reg (" ++ bluespecType ++ ")" - , " " ++ varName ++ " = " ++ regName - ]) + map + (\(_bluespecType, varName, regName, _inputVecName, _inputVals) -> + " " ++ varName ++ "_action = return " ++ regName) vars inputUpdates :: [String] From 3114a448fd3837051bfb35ed5a38a7cc7db0a391 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 3 Nov 2025 06:30:28 -0500 Subject: [PATCH 247/483] copilot-bluespec: Update DESIGN.md to reflect new input/output order. Refs #677. The current Bluespec backend leads to Verilog code that requires manual manipulations in order to work correctly. Specifically, Copilot externs, which are inputs to the Copilot monitoring system, are treated as _outputs_ in Verilog, and Copilot triggers, which can be considered outputs of the monitoring system, are treated as _inputs_. This order of inputs and outputs is the opposite what we would like users to work with, and of what other backends generate (e.g., C99). A prior commit has updated the internals of `copilot-bluespec` to invert the order in which Verilog inputs and outputs are declared. This commit updates the prose in the `DESIGN.md` document to reflect the new design. The new design is just different enough from the old one that it made sense to completely rewrite certain portions of the document. One of the more notable changes is that there are now separate sections about running the generated Verilog code on hardware versus simulating the generated code in software, as these are distinct use cases that require separate discussion. --- copilot-bluespec/DESIGN.md | 799 ++++++++++++++++++++++++------------- 1 file changed, 527 insertions(+), 272 deletions(-) diff --git a/copilot-bluespec/DESIGN.md b/copilot-bluespec/DESIGN.md index dffb607ac..b5a07dd31 100644 --- a/copilot-bluespec/DESIGN.md +++ b/copilot-bluespec/DESIGN.md @@ -5,7 +5,7 @@ compiles a Copilot specification to a Bluespec program. We assume that you already have some familiarity with core Copilot concepts such as streams, triggers, externs, etc. -## Bluespec overview +# Bluespec overview Bluespec is a high-level hardware design language (HDL). Bluespec comes with a compiler, `bsc`, as well as its own simulator, which allows users to run @@ -25,7 +25,7 @@ Bluespec SystemVerilog. This is primarily because BH's Haskell-like syntax is a closer fit to how Copilot is structured, which makes the design of the compiler simpler. -## A small example +# A small example To get a sense for how `copilot-bluespec` works, let us compile a simple Copilot specification down to Bluespec. Our specification will declare a stream @@ -67,224 +67,309 @@ are: 2. The call to `compile "Fibs" spec'` in `main`. This will compile the Copilot specification to a Bluespec program named `Fibs.bs`. -Running this program will generate five files[^1]: +Running this program will generate four files[^1]: [^1]: The actual code in these files is machine-generated and somewhat difficult to read. We have cleaned up the code slightly to make it easier to understand. -* `FibsTypes.bs`: If the Copilot specification contains any structs, then this - file will contain the corresponding Bluespec struct definitions. The - specification does not contain any structs, so this file is mostly empty: +### `Fibs.bs` - ```bluespec - package FibsTypes where +This defines the majority of the Bluespec code needed to implement a monitor: - import FloatingPoint - import Vector - ``` +```bluespec +package Fibs where - Later in this document, we will see a different example that makes use of - structs. Note that the syntax used here is much like in Haskell, with a - notable difference being that Bluespec uses the `package` keyword instead of - `module`. +import FloatingPoint +import Vector -* `FibsIfc.bs`: This file defines a _module interface_ `FibsIfc`, whose methods - correspond to the names of the triggers in the Copilot spec: +import FibsTypes +import BluespecFP - ```bluespec - package FibsIfc where +interface FibsIfc {-# always_ready, always_enabled #-} = + even_guard :: Bool + even_arg0 :: UInt 32 + odd_guard :: Bool + odd_arg0 :: UInt 32 - import FloatingPoint - import Vector +{-# properties mkFibs = { verilog } #-} +mkFibs :: Module FibsIfc +mkFibs = + module + s0_0 :: Reg (UInt 32) <- mkReg 1 + s0_1 :: Reg (UInt 32) <- mkReg 1 + let s0 :: Vector.Vector 2 (Reg (UInt 32)) + s0 = update (update newVector 0 s0_0) 1 s0_1 + s0_idx :: Reg (Bit 64) <- mkReg 0 - import FibsTypes + let s0_get :: Bit 64 -> UInt 32 + s0_get x = (select s0 ((s0_idx + x) % 2))._read - interface FibsIfc = - even :: UInt 32 -> Action - odd :: UInt 32 -> Action - ``` + s0_gen :: UInt 32 + s0_gen = s0_get 0 + s0_get 1 - An interface like `FibsIfc` can be thought of as a special form of data type - that describes how a module (a hardware object) interacts with the - surrounding environment. In order for an application to make use of a Copilot - monitor, it must instantiate `FibsIfc`'s methods. Each method takes a `UInt - 32` (an unsigned 32-bit integer) as an argument and return an `Action`, which - is the Bluespec type for expressions that act on the state of the circuit (at - circuit execution time). Possible `Action`s include reading from and writing - to registers, as well as printing messages. + rules + "step": when True ==> + action + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 - We will see an example of how to instantiate `FibsIfc` later. + interface FibsIfc + even_guard :: Bool + even_guard = + (s0_get 0 % 2) == 0 -* `Fibs.bs`: This file defines a `mkFibs` function, which orchestrates - everything in the generated Bluespec monitor: + even_arg0 :: UInt 32 + even_arg0 = s0_get 0 - ```bluespec - package Fibs where + odd_guard :: Bool + odd_guard = + not (s0_get 0 % 2 == 0) - import FloatingPoint - import Vector + odd_arg0 :: UInt 32 + odd_arg0 = s0_get 0 - import FibsTypes - import FibsIfc - import BluespecFP +interface FibsRulesIfc = + even_action :: UInt 32 -> Action + odd_action :: UInt 32 -> Action - mkFibs :: Module FibsIfc -> Module Empty - mkFibs ifcMod = - module - ifc <- ifcMod +mkFibsRules :: FibsIfc -> FibsRulesIfc -> Rules +mkFibsRules ifc ifcRules = + rules + "even": when ifc.even_guard ==> + ifcRules.even_action ifc.even_arg0 - s0_0 :: Reg (UInt 32) <- mkReg 1 - s0_1 :: Reg (UInt 32) <- mkReg 1 - let s0 :: Vector 2 (Reg (UInt 32)) - s0 = update (update newVector 0 s0_0) 1 s0_1 - s0_idx :: Reg (Bit 64) <- mkReg 0 + "odd": when ifc.odd_guard ==> + ifcRules.odd_action ifc.odd_arg0 - let s0_get :: Bit 64 -> UInt 32 - s0_get x = (select s0 ((s0_idx + x) % 2))._read +addFibsRules :: FibsIfc -> FibsRulesIfc -> Module Empty +addFibsRules ifc ifcRules = addRules (mkFibsRules ifc ifcRules) +``` - s0_gen :: UInt 32 - s0_gen = s0_get 0 + s0_get 1 +Those familiar with Haskell will likely recognize much of the syntax used here, +although it has some minor differences (e.g., using the `package` keyword +instead of `module`, which means something else in Bluespec). We will go into +the parts of the syntax that are unique to Bluespec as they come up. + +The `Fibs.bs` file consists of five definitions: + +1. `FibsIfc`, which is a _module interface_. An interface like `FibsIfc` can be + thought of as a special form of data type that describes how a module (a + hardware object) interacts with the surrounding environment. In order for an + application to make use of a Copilot monitor, it must instantiate + `FibsIfc`'s methods. We will see an example of how to instantiate `FibsIfc` + momentarily. + + The methods of `FibsIfc` correspond to the names of the triggers in the + Copilot spec. Each trigger has a corresponding _guard_ method, which always + has the type `Bool`, and an _argument_ method for each argument that the + trigger accepts, which has a type corresponding to the type of the argument. + We will see how these work in more detail shortly. For now, think of these + methods as being the "outputs" of the monitor program. + +2. `mkFibs`, which returns a module of type `Module FibsIfc`. A module can be + thought of as a generator of hardware objects. The module uses the `FibsIfc` + interface, which indicates that the implementation of the module + instantiates all of the methods in `FibsIfc`. If you squint, you'll + recognize bits and pieces of the original Copilot spec, such as the part + that checks for even stream elements in `even_guard`. We will go over the + individual definitions used in the implementation of `mkFibs` later. + + Note `mkFibs`'s use of the `module` and `action` keywords. These can be + thought of as specialized versions of Haskell's `do`-notation, where + `module` denotes the `Module` monad, and `action` denotes the `ActionValue` + monad. (Note that `Action` is an alias for `ActionValue ()`.) The `Module` + monad describes how to elaborate the structure of a module, whereas the + `ActionValue` monad describes the behavior of a circuit at execution time. + + Another piece of syntax that is unique to Bluespec modules is the `rules` + keyword, which denote rules that may or may not be run on each clock cycle + of the hardware. This particular module only has a single rule, named + `step`, which updates some internal state used by the module. That internal + state is then used to define the values that the interface methods return. + The `when True` part of the rule indicates that it always runs on every + clock cycle. + +3. `FibsRulesIfc` is another module interface, with one method for each of the + Copilot spec's triggers. Each method takes arguments corresponding to the + types of the triggers' arguments and returns a value of type `Action`. As + the name `FibsRulesIfcs` suggests, these methods are intended to be used to + build up rule definitions, and we will see an example of this shortly. + + As an aside, you may be wondering why there are two different interfaces + that each have methods corresponding to the Copilot spec's trigger + functions. Why not just have a single interface instead? The short answer to + this question is that these interfaces are intended for different use cases: + modules that instantiate `FibsRulesIfc` are intended for simulation, whereas + modules that instantiate `FibsIfc` are intended for Verilog code generation. + For a longer answer to this question, see the "Generating Verilog code" + section below. + +4. `mkFibsRules`, a function which takes two values that instantiate the + `FibsIfc` and `FibsRulesIfc` interfaces and returns a `Rules` value. A value + of type `Rules` is just a set of rules, which can be passed around in + Bluespec like any other value. + + `mkFibsRules` defines two rules, one for each trigger function in the + original Copilot spec. Unlike the `step` rule seen earlier, these rules may + not be run on every clock cycle. Specifically, the `even` rule will only run + if the `ifc.even_guard` expression returns `True` on a given clock cycle, + and the `odd` rule will only run if `ifc.odd_guard` returns `True` on a + given clock cycle. If one of these rules are run, then they will invoke the + corresponding method from the `FibsRulesIfc` interface, passing in the + corresponding `*_arg` method(s) from the `FibsIfc`. + +5. `addFibsRules`, a function which is very similar to `mkFibsRules`, except + that it has the return type `Module Empty` instead of `Rules`. All that + `addFibsRules` does is invoke `mkFibsRules` and then pass it to Bluespec's + `addRules` function, which actually adds the rules to a module. This is a + convenience that allows users to quickly define a full Bluespec module that + is suitable for simulation purposes (see the "Simulating Bluespec code" + section below). + + Note that the module that this returns instantiates the `Empty` interface. + `Empty` is a standard interface with no methods at all, and `Empty` is + typically used in top-level Bluespec modules that act as entrypoints to + simulation. + +### `FibsTypes.bs` + +If the Copilot specification contained any structs, then the generated +`FibsTypes.bs` file would contain the corresponding Bluespec struct +definitions. The specification in `Fibs.hs` does not contain any structs, so +this file is mostly empty: - even_guard :: Bool - even_guard = - (s0_get 0 % 2) == 0 +```bluespec +package FibsTypes where - odd_guard :: Bool - odd_guard = - not (s0_get 0 % 2 == 0) +import FloatingPoint +import Vector +``` - rules - "even": when even_guard ==> - ifc.even (s0_get 0) +Later in this document, we will see a different example that makes use of +structs. - "odd:": when odd_guard ==> - ifc.odd (s0_get 0) +### `BluespecFP.bsv` - "step": when True ==> - action - select s0 s0_idx := s0_gen - s0_idx := (s0_idx + 1) % 2 - ``` +This is a collection of floating-point operations that leverage BDPI +(Bluespec's foreign-function interface). We will omit the full contents of this +file for brevity, but it will look something like this: - `mkFibs` returns a module, which can be thought of as a generator of hardware - objects. `mkFibs` takes a `FibsIfc` module as an argument and returns another - module, which is parameterized by `Empty`. `Empty` is a standard interface - with no methods, and `Empty` is typically used in top-level modules that act - as program entrypoints. +```bluespec +import FloatingPoint::*; - Note that the `module` and `action` keywords can be thought of as specialized - versions of Haskell's `do`-notation, where `module` denotes the `Module` - monad, and `action` denotes the `ActionValue` monad. (Note that `Action` is - an alias for `ActionValue ()`.) The `Monad` monad describes how to elaborate - the structure of a module, whereas the `ActionValue` monad describes the - behavior of a circuit at execution time. - -* `BluespecFP.bsv`: A collection of floating-point operations that leverage BDPI - (Bluespec's foreign-function interface). We will omit the full contents of - this file for brevity, but it will look something like this: - - ```bluespec - import FloatingPoint::*; - - import "BDPI" function Float bs_fp_expf (Float x); - import "BDPI" function Double bs_fp_exp (Double x); - import "BDPI" function Float bs_fp_logf (Float x); - import "BDPI" function Double bs_fp_log (Double x); - ... - ``` +import "BDPI" function Float bs_fp_expf (Float x); +import "BDPI" function Double bs_fp_exp (Double x); +import "BDPI" function Float bs_fp_logf (Float x); +import "BDPI" function Double bs_fp_log (Double x); +... +``` - For more information on what this file does, see the "Floating-point numbers" - section below. +For more information on what this file does, see the "Floating-point numbers" +section below. -* `bs_fp.c`: A collection of floating-point operations implemented in C. These - functions are imported via BDPI in `BluespecFP.bsv`. We will omit the full - contents of this file for brevity, but it will look something like this: +### `bs_fp.c` - ```bluespec - #include +A collection of floating-point operations implemented in C. These functions are +imported via BDPI in `BluespecFP.bsv`. We will omit the full contents of this +file for brevity, but it will look something like this: - union ui_float { - unsigned int i; - float f; - }; +```bluespec +#include - union ull_double { - unsigned long long i; - double f; - }; +union ui_float { + unsigned int i; + float f; +}; - unsigned int bs_fp_expf(unsigned int x) { - ... - } +union ull_double { + unsigned long long i; + double f; +}; - unsigned long long bs_fp_exp(unsigned long long x) { - ... - } +unsigned int bs_fp_expf(unsigned int x) { + ... +} - unsigned int bs_fp_logf(unsigned int x) { - ... - } +unsigned long long bs_fp_exp(unsigned long long x) { + ... +} - unsigned long long bs_fp_log(unsigned long long x) { - ... - } +unsigned int bs_fp_logf(unsigned int x) { + ... +} +unsigned long long bs_fp_log(unsigned long long x) { ... - ``` +} + +... +``` + +For more information on what this file does, see the "Floating-point numbers" +section below. + +## Using the generated Bluespec code + +What do we do with the Bluespec code after generating it? The answer to this +question ultimately depends on your intended use case. Sometimes, it can be +useful to just simulate the Bluespec code in software, which we can do with a +variety of hardware simulators. In other use cases, it can be more useful to +directly synthesize code that can be embedded in real hardware. We will review +each of these workflows below. + +### Simulating Bluespec code - For more information on what this file does, see the "Floating-point numbers" - section below. +Hardware simulation allows us to model the behavior of hardware without +actually needing access to physical hardware to do so. There are a number of +different simulators out there, such as Bluespec's own Bluesim simulator, as +well as Verilog-specific simulators such as Verilator and Icarus Verilog +(`iverilog`). We will be using Bluesim throughout this section, as it is +already bundled with each installation of Bluespec. -In a larger application, a Copilot user would instantiate `mkFibs` with a -`FibsIfc` module that describes what should happen when the `even` and `odd` -triggers fire. `FibsIfc` contains everything that the user must supply; -everything else is handled within the module that `mkFibs` returns. +Suppose we have a Bluespec program that we want to monitor with Copilot. This +program will likely have a top-level module (typically of type `Module Empty`), +and it is the role of the program's developer to decide what action the program +should take whenever the monitor fires a trigger. In a full hardware setting, +these actions might constitute something like blinking an LED or displaying a +message on a seven-segment display. For simulation purposes, however, we can +approximate this behavior by doing something simpler (e.g., printing messages +to standard output on the terminal). -Here is an example of a larger application might look like: +Here is an example of a program that uses the Copilot-generated `Fibs.bs` +monitor shown above: ```bluespec package Top where import Fibs -import FibsIfc import FibsTypes -fibsIfc :: Module FibsIfc -fibsIfc = - module - interface - even x = - $display "Even Fibonacci number: %0d" x - - odd x = - $display "Odd Fibonacci number: %0d" x - mkTop :: Module Empty -mkTop = mkFibs fibsIfc +mkTop = + module + fibsMod <- mkFibs + addFibsRules fibsMod $ + interface FibsRulesIfc + even_action x = + $display "Even Fibonacci number: %0d" x + + odd_action x = + $display "Odd Fibonacci number: %0d" x ``` `mkTop` is the top-level module that we will use as a program entrypoint. The -only interesting thing that it does is instantiate `mkFibs` with a custom -`FibsIfc`, where `even` and `odd` are defined to display a custom message -whenever an even or odd Fibonacci number is encountered, respectively. +only interesting thing that it does is create a `FibsIfc` module internally +(using `mkFibs`) and then define the actions to take when the `even` or `odd` +triggers fire. These actions are defined by the implementations of the +`even_action` and `odd_action` methods, respectively, which print custom +messages to standard output. -We can run `mkTop` by using Bluespec's simulator like so: +We can run `mkTop` by using Bluesim like so: ``` $ bsc -sim -g mkTop -u Top.bs -checking package dependencies -compiling Top.bs -code generation for mkTop starts -Elaborated module file created: mkTop.ba -All packages are up to date. - $ bsc -sim -e mkTop -o mkTop.exe bs_fp.c -Bluesim object created: mkTop.{h,o} -Bluesim object created: model_mkTop.{h,o} -Simulation shared library created: mkTop.exe.so -Simulation executable created: mkTop.exe - $ ./mkTop.exe -m 10 Odd Fibonacci number: 1 Odd Fibonacci number: 1 @@ -304,6 +389,94 @@ the values of registers do not become ready until after the reset signal is enabled. Because all of the rules depend on registers, none of them will fire until the second clock cycle or later. +### Generating Verilog code + +Generating code that is suitable for hardware is a somewhat different problem +than generating code that is suitable for simulation. Many types of hardware +(e.g., FPGAs) require specific hardware description languages in order to +synthesize hardware circuits, such as Verilog or VHDL. Bluespec supports +compiling to Verilog, so we will be focusing on that in this section. + +A typical Verilog module has a number of _signals_ that represent the paths +into a circuit (called _inputs_), within a circuit (called _wires_), and +outside of the circuit (called _outputs_). The generated Bluespec code seen +above has been carefully designed such that the triggers in the Copilot spec +compile directly to Verilog outputs. (Copilot also has a notion of external +streams that compile to Verilog inputs; see the "External streams" section for +more on this.) + +Unlike the simulation use case, generating Verilog code does not make use of +the `FibsRulesIfc` interface at all, as this is a convenience that is only +provided to make simulation easier. Instead, we compile the `mkFibs` module +directly to Verilog: + +``` +$ bsc -verilog -g mkFibs -u Fibs.bs +``` + +This generates a Verilog module `mkFibs.v` that looks like the following: + +```verilog +module mkFibs(CLK, + RST_N, + + even_guard, + + even_arg0, + + odd_guard, + + odd_arg0); + input CLK; + input RST_N; + + // value method even_guard + output even_guard; + + // value method even_arg0 + output [31 : 0] even_arg0; + + // value method odd_guard + output odd_guard; + + // value method odd_arg0 + output [31 : 0] odd_arg0; + + ... + +endmodule // mkFibs +``` + +We show only a subset of the generated Verilog code to highlight the inputs and +outputs. There are two inputs corresponding to the `CLK` (clock) and `RST_N` +(reset) signals, which are generated for all Copilot monitors, regardless of +what the underlying spec is. The outputs correspond to the methods of the +`FibsIfc` interface. For instance, the signal in the `even_guard` output +indicates whether the `even` trigger fires on a given clock cycle or not, and +the value in the `even_arg0` output indicates what argument was passed to the +trigger on that clock cycle. These outputs can then be hooked up to another +part of a hardware circuit (e.g., LEDs) that can respond to these values as +needed. + +In general, if a Bluespec interface has a method of type `t` (where `t` is a +non-monadic type), then that method will be compiled to a Verilog output. If a +method has type `t -> Action`, however, then that method will be compiled to a +Verilog input instead. This is a key reason why it is necessary to separate +`FibsIfc` from `FibsRulesIfc`. If we compiled a `Module FibsRulesIfc` to +Verilog, then the triggers would become inputs, not outputs! + +(As a side note: the generated Bluespec code also employs various other tricks +to get the compiled Verilog code to look the way it does. For instance, note +the `{-# properties mkFibs = { verilog } #-}` pragma above `mkFibs`, which +instructs the Bluespec compiler to synthesize Verilog code for it. Also note +the `{-# always_ready, always_enabled #-}` attributes used in `FibsIfc`, which +ensure that the generated Verilog code does not contain unwanted "ready" or +"enabled" signals as inputs.) + +For a more in-depth tutorial on how to use the generated Verilog code on an +actual FPGA, see [this +tutorial](https://github.com/Copilot-Language/copilot/blob/master/copilot/examples/fpga/HelloWorld/README.md). + ## Streams Much like in `copilot-c99`, `copilot-bluespec` translates each stream @@ -352,8 +525,8 @@ In order to access an element of a stream, we make use of the `s0_get` function: ```bluespec - let s0_get :: Bit 64 -> UInt 32 - s0_get x = (select s0 ((s0_idx + x) % 2))._read + let s0_get :: Bit 64 -> UInt 32 + s0_get x = (select s0 ((s0_idx + x) % 2))._read ``` This will use `s0_idx` and an offset `x` to compute which `Reg` in the `Vector` @@ -362,48 +535,43 @@ to read from. ## Rules The _rules_ govern what actions are performed during each cycle. There are -three actions in the `Fibs.hs` example: +three actions in the `Fibs.bs` example above: one in `mkFibs`: ```bluespec rules - "even": when even_guard ==> - ifc.even (s0_get 0) + "step": when True ==> + action + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 +``` + +And two more in `mkFibsRules`: - "odd:": when odd_guard ==> - ifc.odd (s0_get 0) +```bluespec + rules + "even": when ifc.even_guard ==> + ifcRules.even_action ifc.even_arg0 - "step": when True ==> do - select s0 s0_idx := s0_gen - s0_idx := (s0_idx + 1) % 2 + "odd": when ifc.odd_guard ==> + ifcRules.odd_action ifc.odd_arg0 ``` Each rule consists of three parts: -1. A label (e.g., `"even"`) that uniquely identifies the rule within the +1. A label (e.g., `"step"`) that uniquely identifies the rule within the module. 2. An explicit condition, which is a boolean expression of the form `when ==> ...`. In order for a rule to fire on a given clock cycle, its explicit condition `` must hold. -3. A rule body (e.g., `ifc.even (s0_get 0)`), which describes what action the - rule performs if it fires on a given clock cycle. +3. A rule body (of type `Action`), which describes what action the rule + performs if it fires on a given clock cycle. In the example above, the `"even"` and `"odd"` rules govern the behavior of the triggers in the Copilot specification. These rules will only fire if -`even_guard` or `odd_guard` hold, and if they fire, they will call the `even` -or `odd` method of `FibsIfcs`, respectively. The definitions of `even_guard` -and `odd_guard` are: - -```bluespec - even_guard :: Bool - even_guard = - (s0_get 0 % 2) == 0 - - odd_guard :: Bool - odd_guard = - not (s0_get 0 % 2 == 0) -``` +`even_guard` or `odd_guard` hold, and if they fire, they will call the +`even_action` or `odd_action` method of `FibsRulesIfc`, respectively. The `"step"` rule is the heart of the Copilot specification, and it always runs on each clock cycle. `"step"` does two things: @@ -412,8 +580,8 @@ on each clock cycle. `"step"` does two things: which is defined like so: ```bluespec - s0_gen :: UInt 32 - s0_gen = s0_get 0 + s0_get 1 + s0_gen :: UInt 32 + s0_gen = s0_get 0 + s0_get 1 ``` 2. It increments `s0_idx`, making sure to wrap around to `0` if its value @@ -433,27 +601,28 @@ however. Recall the definitions of the three rules in the example program above: ```bluespec - rules - "even": when even_guard ==> - ifc.even (s0_get 0) +"step": when True ==> + action + select s0 s0_idx := s0_gen + s0_idx := (s0_idx + 1) % 2 - "odd:": when odd_guard ==> - ifc.odd (s0_get 0) +"even": when ifc.even_guard ==> + ifcRules.even_action ifc.even_arg0 - "step": when True ==> do - select s0 s0_idx := s0_gen - s0_idx := (s0_idx + 1) % 2 +"odd": when ifc.odd_guard ==> + ifcRules.odd_action ifc.odd_arg0 ``` If this were a language like C, then the order in which the rules appear would -have an effect on the runtime semantics of the program, as the behavior of -`s0_get` (used in the `"even"` and `"odd"` rules) depends on the current value -of `s0` and `s0_idx`. In Bluespec, however, the order of these rules do _not_ -matter. The `"step"` rule performs side effects of updating the value of `s0` -and `s0_idx`, but these effects are performed in a single, atomic transaction -at the end of the clock cycle. This means that the values of `s0` and `s0_idx` -that `s0_get` sees will always match their values at the start of the clock -cycle, regardless of what order Bluespec uses to invoke the rules. +have an effect on the runtime semantics of the program, as the values that +`even_arg0` and `odd_arg0` return (used in the `"even"` and `"odd"` rules) +depends on the current value of `s0` and `s0_idx`. In Bluespec, however, the +order of these rules do _not_ matter. The `"step"` rule performs side effects +of updating the value of `s0` and `s0_idx`, but these effects are performed in +a single, atomic transaction at the end of the clock cycle. This means that the +values of `s0` and `s0_idx` that `s0_get` sees will always match their values +at the start of the clock cycle, regardless of what order Bluespec uses to +invoke the rules. Because the Bluespec compiler can pick whatever rule order it wishes to, this can impact the order in which `$display` output is presented. This doesn't @@ -466,8 +635,11 @@ specified. ## External streams The example above sampled data from a stream that was defined within the -Copilot specification. To see what happens when a specification uses an -external stream, let's make one small change to the example: +Copilot specification. Copilot also features a notion of _external_ streams, +which sample data from the surrounding environment. (For instance, one could +populate an external stream with data read from a sensor in a real-world +application.) To see what happens when a specification uses an external stream, +let's make one small change to the example above: ```diff fibs :: Stream Word32 @@ -476,68 +648,117 @@ external stream, let's make one small change to the example: ``` Now let's re-run the example and inspect the resulting Bluespec program. There -are two notable changes worth commenting on. The first change is in -`FibsIfc.bs`: +are several notable changes worth commenting on. The first change is in the +definition of the `FibsIfc` interface: ```bluespec -package FibsIfc where +interface FibsIfc {-# always_ready, always_enabled #-} = + even_guard :: Bool + even_arg0 :: UInt 32 + odd_guard :: Bool + odd_arg0 :: UInt 32 + fibs :: UInt 32 -> Action {-# prefixs = "", arg_names = [fibs] #-} +``` -import FloatingPoint -import Vector +This time, there is an additional `fibs` method of type `UInt 32 -> Action`. +This type is carefully chosen such that when `FibsIfc` is compiled to Verilog, +`fibs` will be represented as an input to the Verilog module, which intuitively +matches how external streams are used in Copilot itself. (Also note the +attributes to the right of the type signature, which ensure that the generated +Verilog input has precisely the name "`fibs`".) -import FibsTypes +The next change is in `mkFibs`, which demonstrates how the `fibs` method is +instantiated: + +```bluespec +{-# properties mkFibs = { verilog } #-} +mkFibs :: Module FibsIfc +mkFibs = + module + fibs_wire :: Wire (UInt 32) <- mkBypassWire + + interface FibsIfc + even_guard :: Bool + even_guard = + (fibs_wire._read % 2) == 0 + + even_arg0 :: UInt 32 + even_arg0 = fibs_wire._read -interface FibsIfc = - even :: UInt 32 -> Action - odd :: UInt 32 -> Action - fibs :: Reg (UInt 32) + odd_guard :: Bool + odd_guard = + not (fibs_wire._read % 2 == 0) + + odd_arg0 :: UInt 32 + odd_arg0 = fibs_wire._read + + fibs val = fibs_wire := val ``` -This time, the `FibsIfc` interface has an additional `fibs` method of type `Reg -(UInt 32)`. This register corresponds to the external stream that we just -defined, and it is the responsibility of the application which instantiates -`FibsIfc` to dictate what values should be written to the register. +This looks quite a bit different! Let's go over what has changed: -The second change is in `Fibs.bs`: +* There are no longer any registers to hold internal state, nor is there a + `"step"` rule that updates the registers. These registers were ultimately + used in service of maintaining the ring buffers that comprised the definition + of the `fibs` stream that was defined within Copilot. Now that `fibs` is an + external stream, none of the ring buffer machinery is needed anymore. -```bluespec -package Fibs where + (Note that this is not an all-or-nothing proposition, as it is possible to + have a Copilot spec that combines defined-within-Copilot streams and external + streams.) -import FloatingPoint -import Vector +* There is a new piece of internal state: the `fibs_wire` `Wire`. Moreover, + all places where `s0_get 0` was called before have now been replaced with + `fibs_wire._read`. A `Wire` is essentially a special type of register that + compiles to more compact Verilog code. -import FibsTypes -import FibsIfc +* Finally, there is now an implementation of the `fibs` method, which writes + the supplied value to `fibs_wire`. This is what allows other methods to see + the most recently sampled value in the `fibs` external stream. -mkFibs :: Module FibsIfc -> Module Empty -mkFibs ifcMod = - module - ifc <- ifcMod +The next change is in the definition of `FibsRulesIfc`, which has also gained +another method corresponding to the `fibs` external stream: - let even_guard :: Bool - even_guard = - (ifc.fibs._read % 2) == 0 +```bluespec +interface FibsRulesIfc = + even_action :: UInt 32 -> Action + odd_action :: UInt 32 -> Action + fibs_action :: ActionValue (UInt 32) +``` - odd_guard :: Bool - odd_guard = - not (ifc.fibs._read % 2 == 0) +Unlike `even_action` and `odd_action`, which denote an action for _consuming_ a +value, the type of `fibs_action` (`ActionValue (UInt 32)`) denotes how to +_produce_ a value. This makes sense, since the application being monitored +ultimately has to be the source of the values that populate the external +stream. The implementation of the `fibs_action` method is where this population +happens. - rules - "even": when even_guard ==> - ifc.even ifc.fibs._read +Finally, the definition of `mkFibsRules` now implements a `"fibs"` rule: - "odd:": when odd_guard ==> - ifc.odd ifc.fibs._read +```bluespec +mkFibsRules :: FibsIfc -> FibsRulesIfc -> Rules +mkFibsRules ifc ifcRules = + rules + "even": when ifc.even_guard ==> + ifcRules.even_action ifc.even_arg0 + + "odd": when ifc.odd_guard ==> + ifcRules.odd_action ifc.odd_arg0 + + "fibs": when Prelude.True ==> + action + fibsVal <- ifcRules.fibs_action + ifc.fibs fibsVal ``` -This time, the only stream that is referenced is `ifc.fibs`. As a consequence, -the `s0` stream, the `s0_idx` index, and the `"step"` rule are no longer -necessary, making the code considerably simpler. +This new rule produces a external value using `ifcRules.fibs_action` and then +feeds it to `ifc.fibs` as an input. + +### Simulating Bluespec code involving external streams -The flip side to having the generated code be simpler is that the application -which uses `mkFibs` must now do additional work to specify what the value of -`ifc.fibs` is. For example, we can port the behavior of the previous version -of the program like so: +Let's update our example from the "Simulating Bluespec code" section to +accommodate `fibs` becoming an external stream. Here is an example of what a +`Top.bs` application might look like: ```bluespec package Top where @@ -545,11 +766,10 @@ package Top where import Vector import Fibs -import FibsIfc import FibsTypes -fibsIfc :: Module FibsIfc -fibsIfc = +mkTop :: Module Empty +mkTop = module s0_0 :: Reg (UInt 32) <- mkReg 1 s0_1 :: Reg (UInt 32) <- mkReg 1 @@ -563,38 +783,73 @@ fibsIfc = s0_gen :: UInt 32 s0_gen = s0_get 0 + s0_get 1 - fibs_impl :: Reg (UInt 32) <- mkReg 1 + fibsMod <- mkFibs - interface - even x = - $display "Even Fibonacci number: %0d" x + addFibsRules fibsMod $ + interface FibsRulesIfc + even_action x = + $display "Even Fibonacci number: %0d" x - odd x = - $display "Odd Fibonacci number: %0d" x + odd_action x = + $display "Odd Fibonacci number: %0d" x - fibs = fibs_impl + fibs_action = return (s0_get 0) rules - "fibs": when True ==> - fibs_impl := s0_get 1 - "step": when True ==> action select s0 s0_idx := s0_gen s0_idx := (s0_idx + 1) % 2 +``` -mkTop :: Module Empty -mkTop = mkFibs fibsIfc +Note that all of the code involving registers and the `"step"` rule now lives +in `Top.bs`, as it is now the responsibility of the application to produce the +values for the `fibs` stream. There is also now an implementation of the +`fibs_action` method that returns the most recently computed Fibonacci number +on each clock cycle. + +### Generating Verilog code involving external streams + +Compiling the new version of `mkFibs` to Verilog produces a `mkFibs.v` file +that looks like the following: + +```verilog +module mkFibs(CLK, + RST_N, + + even_guard, + + even_arg0, + + odd_guard, + + odd_arg0, + + fibs); + input CLK; + input RST_N; + + // value method even_guard + output even_guard; + + // value method even_arg0 + output [31 : 0] even_arg0; + + // value method odd_guard + output odd_guard; + + // value method odd_arg0 + output [31 : 0] odd_arg0; + + // action method fibs + input [31 : 0] fibs; + + ... + +endmodule // mkFibs ``` -The code involving `s0`, `s0_idx`, and the `"step"` rule is exactly as before. -The only notable difference is that we create an additional `fibs_impl` -register, declare `fibs = fibs_impl` when instantiating the `FibsIfc` -interface, and declare an additional `"fibs"` rule to update the value of -`fibs_impl` on each clock cycle. Note that we always set the value of -`fibs_impl` to be `s0_get 1`, which corresponds to the most recently computed -Fibonacci number. If we set `fibs_impl` to be `s0_get 0` instead, then -`fibs_impl` would lag behind by one cycle. (Try it.) +As expected, the only notable change is that there is now a `fibs` input. # Notes From dab5669f4459bcea5a624b4357c46552c8f40c49 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 3 Nov 2025 06:55:48 -0500 Subject: [PATCH 248/483] copilot-bluespec: Document changes in CHANGELOG. Refs #677. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index e7472493b..17c30d99e 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-03 + * Flip direction of interface inputs and outputs in Bluespec. (#677) + 2025-09-07 * Version bump (4.5.1). (#666) From de6c50a5fbad1bf73971c2a77b52073ae94d4077 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 249/483] copilot-core: Version bump (4.6). Refs #679. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 78e2d5808..6c18d6ca3 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.5.1 +version: 4.6 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From 60779af32db12faec753518a8e266b7ab4874280 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 250/483] copilot-bluespec: Version bump (4.6). Refs #679. --- copilot-bluespec/copilot-bluespec.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index f62cb2432..975868aca 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.5.1 +version : 4.6 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -44,8 +44,8 @@ library , filepath >= 1.4 && < 1.6 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.5.1 && < 4.6 - , language-bluespec >= 0.1 && < 0.2 + , copilot-core >= 4.6 && < 4.7 + , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From 95f3bcc5c5af02d4952033951cfa1610a1b0122d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 251/483] copilot-c99: Version bump (4.6). Refs #679. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index b33f4e5bc..e5efd5f24 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.5.1 +version : 4.6 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.5.1 && < 4.6 + , copilot-core >= 4.6 && < 4.7 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From 19497c5192b9d17c36a2b9ea6e7adec76246e424 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 252/483] copilot-prettyprinter: Version bump (4.6). Refs #679. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index a5acd909f..0f413f63d 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.5.1 +version: 4.6 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.5.1 && < 4.6 + copilot-core >= 4.6 && < 4.7 exposed-modules: From 67d4d52610534e3192881fec42d47c241b306e2a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 253/483] copilot-interpreter: Version bump (4.6). Refs #679. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index b6b2c4fe7..e8b9fad4f 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.5.1 +version: 4.6 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.5.1 && < 4.6 + copilot-core >= 4.6 && < 4.7 exposed-modules: From 1b67a69a58ed80b78bfdc0712f1aa094d051bbd2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 254/483] copilot-theorem: Version bump (4.6). Refs #679. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 86bb1c68c..b830f91a6 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.5.1 +version : 4.6 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -62,8 +62,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.5.1 && < 4.6 - , copilot-prettyprinter >= 4.5.1 && < 4.6 + , copilot-core >= 4.6 && < 4.7 + , copilot-prettyprinter >= 4.6 && < 4.7 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From f57879372d0fc41625a30152e452f79ce7eec3eb Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 255/483] copilot-language: Version bump (4.6). Refs #679. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 7d02c0bfc..543a11770 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.5.1 +version: 4.6 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.5.1 && < 4.6 - , copilot-interpreter >= 4.5.1 && < 4.6 - , copilot-theorem >= 4.5.1 && < 4.6 + , copilot-core >= 4.6 && < 4.7 + , copilot-interpreter >= 4.6 && < 4.7 + , copilot-theorem >= 4.6 && < 4.7 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From 55f945cc71aabdf1d64f01dc2f588e6917ef6d44 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 256/483] copilot-libraries: Version bump (4.6). Refs #679. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 3402b5296..9b17524f4 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.5.1 +version: 4.6 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.8 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.5.1 && < 4.6 + , copilot-language >= 4.6 && < 4.7 exposed-modules: Copilot.Library.Libraries From 0233d7ef064bff68a4bca4d29d4df3248739738a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 257/483] copilot-verifier: Version bump (4.6). Refs #679. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 808ac3823..9c6bed8d5 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.5.1 +Version: 4.6 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.5.1 && < 4.6, - copilot-core >= 4.5.1 && < 4.6, - copilot-theorem >= 4.5.1 && < 4.6, + copilot-c99 >= 4.6 && < 4.7, + copilot-core >= 4.6 && < 4.7, + copilot-theorem >= 4.6 && < 4.7, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.5.1 && < 4.6, - copilot-language >= 4.5.1 && < 4.6, - copilot-libraries >= 4.5.1 && < 4.6, - copilot-prettyprinter >= 4.5.1 && < 4.6, + copilot >= 4.6 && < 4.7, + copilot-language >= 4.6 && < 4.7, + copilot-libraries >= 4.6 && < 4.7, + copilot-prettyprinter >= 4.6 && < 4.7, copilot-verifier exposed-modules: Copilot.Verifier.Examples From e14f76cff149540e9df1d2467b8f47530ac8f19a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 258/483] copilot-visualizer: Version bump (4.6). Refs #679. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index c88f26f1f..6afd9070a 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.5.1 +version: 4.6 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.5.1 && < 4.6, - copilot-core >= 4.5.1 && < 4.6, - copilot-interpreter >= 4.5.1 && < 4.6, - copilot-language >= 4.5.1 && < 4.6 + copilot >= 4.6 && < 4.7, + copilot-core >= 4.6 && < 4.7, + copilot-interpreter >= 4.6 && < 4.7, + copilot-language >= 4.6 && < 4.7 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.5.1 && < 4.6, + copilot >= 4.6 && < 4.7, copilot-visualizer if flag(examples) From 46fe80db107aa4267c131e152005ef9ed8f1543c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:12:29 -0800 Subject: [PATCH 259/483] copilot: Version bump (4.6). Refs #679. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index a15dd5cf3..f52eecc4e 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.5.1 +version: 4.6 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.5.1 && < 4.6 - , copilot-theorem >= 4.5.1 && < 4.6 - , copilot-language >= 4.5.1 && < 4.6 - , copilot-libraries >= 4.5.1 && < 4.6 - , copilot-c99 >= 4.5.1 && < 4.6 - , copilot-prettyprinter >= 4.5.1 && < 4.6 + , copilot-core >= 4.6 && < 4.7 + , copilot-theorem >= 4.6 && < 4.7 + , copilot-language >= 4.6 && < 4.7 + , copilot-libraries >= 4.6 && < 4.7 + , copilot-c99 >= 4.6 && < 4.7 + , copilot-prettyprinter >= 4.6 && < 4.7 exposed-modules: Language.Copilot, Language.Copilot.Main From c0d4bedeb7facdcaa4c1757f359866b0f5bad900 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 260/483] copilot-core: Document changes in CHANGELOG. Refs #679. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 62e89d1ef..b0b94a732 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From bc9cf505dfd905599628f89db79e701e3085a44b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 261/483] copilot-bluespec: Document changes in CHANGELOG. Refs #679. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 17c30d99e..5365300c1 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2025-11-03 +2025-11-07 + * Version bump (4.6). (#679) * Flip direction of interface inputs and outputs in Bluespec. (#677) 2025-09-07 From d3fd856ebd79436ac7cb22a5c8bbe40e7c307ece Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 262/483] copilot-c99: Document changes in CHANGELOG. Refs #679. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index e5f0b606d..8b7be6866 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From d6a6578c1de9a32bd1f85d99b011e726a3e81976 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 263/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #679. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 73ca7c5bf..26b0500ad 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From 5fe6665070954cce96796966a0dfaeab8f92cf37 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 264/483] copilot-interpreter: Document changes in CHANGELOG. Refs #679. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index e819c4162..c6f5b6cbf 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From 43e71617bf485f8c0357a36b1e64474ad9e8e70c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 265/483] copilot-theorem: Document changes in CHANGELOG. Refs #679. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 621417e73..463ef02c0 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) * Remove unused function Copilot.Theorem.Misc.SExpr.parseSExpr. (#661) From da3de0a79182d300f67df8f34c74ca5c5470efd6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 266/483] copilot-language: Document changes in CHANGELOG. Refs #679. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index ed8ebbebb..c22426298 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From c3d752f32f6cc5559f0103e59f73c0ed794f66a4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 267/483] copilot-libraries: Document changes in CHANGELOG. Refs #679. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 5d4323d7f..6f6a6a6bd 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From e09c1b44dfb8accee4aedb5d4505bd9a568503fb Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 268/483] copilot-verifier: Document changes in CHANGELOG. Refs #679. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index e8b89e276..e4b2f9542 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) From 17c7ca00d6fc5220e585bf395d238fe84f2a0941 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 269/483] copilot-visualizer: Document changes in CHANGELOG. Refs #679. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index cf9f151fb..2dbe2a7ae 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) * Relax version constraint on `ogma-extra`. (#645) From 0c5e4477c917061b2ff11ffae07467eb725bd5d2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 7 Nov 2025 22:13:01 -0800 Subject: [PATCH 270/483] copilot: Document changes in CHANGELOG. Refs #679. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 7b69ed049..11069c98a 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2025-11-07 + * Version bump (4.6). (#679) + 2025-09-07 * Version bump (4.5.1). (#666) * Add hello world example for FPGA. (#657) From 031223d9cc94453c8cbccd43397b741c0592855d Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 14 Oct 2025 13:23:49 -0500 Subject: [PATCH 271/483] copilot-bluespec: Disable test suite via newly added manual cabal flag. Refs #673. The copilot-bluespec package requires bsc (the Bluespec compiler) to be installed in order to run its test suite, but the builders that Hackage uses to run uploaded packages' test suites do not have bsc installed, causing Hackage to spuriously claim that copilot-bluespec's tests are failing. This commit adds a cabal flag `test-bluespec` intended to enable tests that require the Bluespec compiler (`bsc`) to run. The default behavior of `cabal test` (with no flags) is now to not build the testsuite, preventing spurious test failures from appearing in Hackage reports. To enable building the testsuite, use: `cabal test -f test-bluespec`. --- copilot-bluespec/copilot-bluespec.cabal | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 975868aca..40ed30938 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -34,6 +34,11 @@ source-repository head type: git location: https://github.com/Copilot-Language/copilot-bluespec.git +flag test-bluespec + description: Enable tests that require the Bluespec compiler (bsc) + default: False + manual: True + library default-language : Haskell2010 hs-source-dirs : src @@ -98,3 +103,8 @@ test-suite tests ghc-options: -Wall + + if flag(test-bluespec) + buildable: True + else + buildable: False From 03fc7d4059d762641c8fccc9076533dca626aa63 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Thu, 4 Dec 2025 15:58:18 -0600 Subject: [PATCH 272/483] copilot-bluespec: Document changes in CHANGELOG. Refs #673. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 5365300c1..931319a86 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2025-12-04 + * Disable test suite via newly added manual cabal flag. (#673) + 2025-11-07 * Version bump (4.6). (#679) * Flip direction of interface inputs and outputs in Bluespec. (#677) From a6f182f4d12e13c610db9318806ce9e6cd6fd62e Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Fri, 19 Dec 2025 11:06:37 -0500 Subject: [PATCH 273/483] copilot-bluespec: Fix translation of constant floating-point values. Refs #696. copilot-bluespec generates syntactically invalid Bluespec code for special floating-point values, such as negative zero, infinity, and NaN values. For instance, it compiles infinity values to Infinity, which is not a valid Bluespec floating-point value. This patch modifies the function (`constFP`) that translates constant floating point values from Copilot to Bluespec so that it correctly handles special cases such as infs, NaNs, and -0.0. --- copilot-bluespec/copilot-bluespec.cabal | 2 + .../src/Copilot/Compile/Bluespec/Expr.hs | 61 +++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 40ed30938..4e1dabc07 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -47,6 +47,8 @@ library build-depends : base >= 4.9 && < 5 , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 + , fp-ieee >= 0.1.0 && < 0.2 + , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 , copilot-core >= 4.6 && < 4.7 diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs index 7297fad2f..39ff1ad22 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs @@ -17,6 +17,8 @@ import Data.Foldable (foldl') import Data.String (IsString (..)) import qualified Language.Bluespec.Classic.AST as BS import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS +import Numeric.Floating.IEEE.NaN (isSignaling, setPayloadSignaling, setPayload, getPayload) +import GHC.Float (double2Float, castFloatToWord32, castDoubleToWord64) -- Internal imports: Copilot import Copilot.Core @@ -611,14 +613,63 @@ constInt ty i -- | Translate a Copilot floating-point literal into a Bluespec expression. constFP :: Type ty -> Double -> BS.CExpr constFP ty d + -- Bluespec internally represents all IEEE floating-point values as structs + -- so special symbols have to be called for constructing special values, + -- e.g. NaN, inf, and -0.0. See + -- https://github.com/B-Lang-org/bsc/blob/main/src/Libraries/Base3-Math/FloatingPoint.bsv#L441 + | isInfinite d = BS.CApply + (BS.CVar $ BS.mkId BS.NoPos "infinity") + [ if d < 0 + then BS.CCon BS.idTrue [] + else BS.CCon BS.idFalse [] + ] + + | isNaN d = let + cexpr = BS.CHasType + ( BS.CLit $ BS.CLiteral BS.NoPos $ BS.LInt $ + BS.IntLit Nothing 10 cval + ) + ( BS.CQType [] $ BS.TCon $ + BS.TyCon + (BS.mkId BS.NoPos ctype) + Nothing + BS.TIabstract + ) + cval = case ty of + Float -> fromIntegral $ + castFloatToWord32 $ uncastDoubleNaNToFloat d + Double -> fromIntegral $ + castDoubleToWord64 d + _ -> impossible "constFP" "copilot-bluespec" + ctype = case ty of + Float -> "Bit 32" + Double -> "Bit 64" + _ -> impossible "constFP" "copilot-bluespec" + in BS.CApply (BS.CVar $ BS.mkId BS.NoPos "unpack") [cexpr] + | d == 0 = BS.CApply + (BS.CVar $ BS.mkId BS.NoPos "zero") + [ if isNegativeZero d + then BS.CCon BS.idTrue [] + else BS.CCon BS.idFalse [] + ] + | d > 0 = withTypeAnnotation ty $ cLit $ BS.LReal d -- Bluespec intentionally does not support negative literal syntax (e.g., -- -42.5), so we must create negative floating-point literals using the -- `negate` function. - | d >= 0 = withTypeAnnotation ty $ cLit $ BS.LReal d - | otherwise = withTypeAnnotation ty $ - BS.CApply - (BS.CVar $ BS.idNegateAt BS.NoPos) - [cLit $ BS.LReal $ negate d] + | otherwise = withTypeAnnotation ty $ + BS.CApply + (BS.CVar $ BS.idNegateAt BS.NoPos) + [cLit $ BS.LReal $ negate d] + +-- Because NaNs are specially placed as bit-representations by `constfp` and `constfp` requires Doubles as inputs, +-- we need to convert NaN doubles safely into NaN floats. GHC Double2Float does not preserve signal nor payload +-- internals of NaNs so in this case we need a special conversion. +uncastDoubleNaNToFloat :: Double -> Float +uncastDoubleNaNToFloat nan = if (isSignaling nan) + then (setPayloadSignaling payload) + else (setPayload payload) + where + payload = double2Float $ getPayload nan -- | Create a Bluespec expression consisting of a literal. cLit :: BS.Literal -> BS.CExpr From f69f8db70774f78580985797ae6dd67cb864e414 Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Fri, 19 Dec 2025 14:37:39 -0500 Subject: [PATCH 274/483] copilot-bluespec: Document changes in CHANGELOG. Refs #696. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 931319a86..168d3ac98 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,5 +1,6 @@ -2025-12-04 +2025-12-19 * Disable test suite via newly added manual cabal flag. (#673) + * Fix translation of constant floating-point values. (#696) 2025-11-07 * Version bump (4.6). (#679) From 4980c6d21dd5aa840a4b78d21286588d56aa6b66 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:30:19 +0000 Subject: [PATCH 275/483] copilot-theorem: Relax version constraints on containers, random. Refs #603. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, containers has seen release 0.8, but copilot-language, copilot-libraries and copilot-theorem need versions strictly lower than 0.8. The same is true for random, of which we depend on versions prior to 1.3 but version 1.3 has been released. This commit relaxes the version constraints on containers and random for the copilot-theorem package. --- copilot-theorem/copilot-theorem.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index b830f91a6..5ae6a507f 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -48,7 +48,7 @@ library build-depends : base >= 4.9 && < 5 , bimap (>= 0.3 && < 0.4) || (>= 0.5 && < 0.6) , bv-sized >= 1.0.2 && < 1.1 - , containers >= 0.4 && < 0.8 + , containers >= 0.4 && < 0.9 , data-default >= 0.7 && < 0.9 , directory >= 1.3 && < 1.4 , libBF >= 0.6.2 && < 0.7 @@ -57,7 +57,7 @@ library , parameterized-utils >= 2.1.1 && < 2.2 , pretty >= 1.0 && < 1.2 , process >= 1.6 && < 1.7 - , random >= 1.1 && < 1.3 + , random >= 1.1 && < 1.4 , transformers >= 0.5 && < 0.7 , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 From b315eb7d10c90214f7f2e8e9484f4361f5f5c9d2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:32:47 +0000 Subject: [PATCH 276/483] copilot-language: Relax version constraint on containers. Refs #603. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, containers has seen release 0.8, but copilot-language, copilot-libraries and copilot-theorem need versions strictly lower than 0.8. The same is true for random, of which we depend on versions prior to 1.3 but version 1.3 has been released. This commit relaxes the version constraint on containers for the copilot-language package. --- copilot-language/copilot-language.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 543a11770..fac90f5e2 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -38,7 +38,7 @@ library build-depends: base >= 4.9 && < 5 , array >= 0.5 && < 0.6 - , containers >= 0.4 && < 0.8 + , containers >= 0.4 && < 0.9 , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 From 4721684bd3564aa972e768c785b7c57045dad7d8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:32:45 +0000 Subject: [PATCH 277/483] copilot-libraries: Relax version constraint on containers. Refs #603. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, containers has seen release 0.8, but copilot-language, copilot-libraries and copilot-theorem need versions strictly lower than 0.8. The same is true for random, of which we depend on versions prior to 1.3 but version 1.3 has been released. This commit relaxes the version constraint on containers for the copilot-libraries package. --- copilot-libraries/copilot-libraries.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 9b17524f4..70fe91cc8 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -38,7 +38,7 @@ library build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.8 + , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 , copilot-language >= 4.6 && < 4.7 From ba8668c38f2ce60f521641f3043e2dafcbf1e0c0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:34:18 +0000 Subject: [PATCH 278/483] copilot: Relax version constraint on optparse-applicative. Refs #603. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, containers has seen release 0.8, but copilot-language, copilot-libraries and copilot-theorem need versions strictly lower than 0.8. The same is true for random, of which we depend on versions prior to 1.3 but version 1.3 has been released. This commit relaxes the version constraint on optparse-applicative for the copilot package. --- copilot/copilot.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index f52eecc4e..d9179eb46 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -48,7 +48,7 @@ library -fno-warn-orphans build-depends: base >= 4.9 && < 5 - , optparse-applicative >= 0.14 && < 0.19 + , optparse-applicative >= 0.14 && < 0.20 , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 From ecd8a9379c77b209a0a2947530fd7d4e9f977cca Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:34:38 +0000 Subject: [PATCH 279/483] copilot-theorem: Document changes in CHANGELOG. Refs #603. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 463ef02c0..1c12b51d7 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Relax version constraint on containers, random. (#603) + 2025-11-07 * Version bump (4.6). (#679) From 2d1b722094da6fc27b3b695f9e673191c5ec6104 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:35:28 +0000 Subject: [PATCH 280/483] copilot-language: Document changes in CHANGELOG. Refs #603. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index c22426298..d6e10102b 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Relax version constraint on containers. (#603) + 2025-11-07 * Version bump (4.6). (#679) From 3894bac73251f19699148fa15c304d5fc5b869c2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:35:47 +0000 Subject: [PATCH 281/483] copilot-libraries: Document changes in CHANGELOG. Refs #603. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 6f6a6a6bd..bc67991ff 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Relax version constraint on containers. (#603) + 2025-11-07 * Version bump (4.6). (#679) From c24a34c88cb1dbaf619ee533717aceaff591f732 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 7 Jan 2026 23:36:10 +0000 Subject: [PATCH 282/483] copilot: Document changes in CHANGELOG. Refs #603. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 11069c98a..759fda729 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Relax version constraint on optparse-applicative. (#603) + 2025-11-07 * Version bump (4.6). (#679) From 9b80035ba4c91d911320b0d6b3891eeec248edfa Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 283/483] copilot-core: Version bump (4.6.1). Refs #705. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 6c18d6ca3..336db0f26 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.6 +version: 4.6.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From ebc8e6f5885ae8ca78dc48aa7d3dfdeca2cd50e9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 284/483] copilot-interpreter: Version bump (4.6.1). Refs #705. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index e8b9fad4f..7395226ab 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.6 +version: 4.6.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.6 && < 4.7 + copilot-core >= 4.6.1 && < 4.7 exposed-modules: From f151430beb695ecdb18448e132c28750c6a2c76e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 285/483] copilot-prettyprinter: Version bump (4.6.1). Refs #705. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 0f413f63d..b303db95f 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.6 +version: 4.6.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.6 && < 4.7 + copilot-core >= 4.6.1 && < 4.7 exposed-modules: From 37c261677e01c42a1d01ea07d99c3988ec9bcc5c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:38 +0000 Subject: [PATCH 286/483] copilot-bluespec: Version bump (4.6.1). Refs #705. --- copilot-bluespec/copilot-bluespec.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 4e1dabc07..b727af7fa 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.6 +version : 4.6.1 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,8 +51,8 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.6 && < 4.7 - , language-bluespec >= 0.1 && < 0.2 + , copilot-core >= 4.6.1 && < 4.7 + , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From bf1135bdc9cb3100612c74262f4f94b9ba7a1570 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 287/483] copilot-c99: Version bump (4.6.1). Refs #705. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index e5efd5f24..40407cec2 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.6 +version : 4.6.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.6 && < 4.7 + , copilot-core >= 4.6.1 && < 4.7 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From abfa7f1ad896e7d266ecc3b1c1ca023b9869dd1e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 288/483] copilot-theorem: Version bump (4.6.1). Refs #705. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 5ae6a507f..173e90eca 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.6 +version : 4.6.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -62,8 +62,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.6 && < 4.7 - , copilot-prettyprinter >= 4.6 && < 4.7 + , copilot-core >= 4.6.1 && < 4.7 + , copilot-prettyprinter >= 4.6.1 && < 4.7 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 5e88c77759e3d39ec4e43893bff49b718322729a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 289/483] copilot-language: Version bump (4.6.1). Refs #705. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index fac90f5e2..0b65b5798 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.6 +version: 4.6.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.6 && < 4.7 - , copilot-interpreter >= 4.6 && < 4.7 - , copilot-theorem >= 4.6 && < 4.7 + , copilot-core >= 4.6.1 && < 4.7 + , copilot-interpreter >= 4.6.1 && < 4.7 + , copilot-theorem >= 4.6.1 && < 4.7 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From e0bae5fcdd3c198561b76b261f2a955c4fbe8f91 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 290/483] copilot-libraries: Version bump (4.6.1). Refs #705. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 70fe91cc8..c864c9cda 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.6 +version: 4.6.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.6 && < 4.7 + , copilot-language >= 4.6.1 && < 4.7 exposed-modules: Copilot.Library.Libraries From 3cd60d0c69fc89e52315be4bc0e0788f767c00d4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:38 +0000 Subject: [PATCH 291/483] copilot-verifier: Version bump (4.6.1). Refs #705. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 9c6bed8d5..f77ebfcaf 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.6 +Version: 4.6.1 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.6 && < 4.7, - copilot-core >= 4.6 && < 4.7, - copilot-theorem >= 4.6 && < 4.7, + copilot-c99 >= 4.6.1 && < 4.7, + copilot-core >= 4.6.1 && < 4.7, + copilot-theorem >= 4.6.1 && < 4.7, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.6 && < 4.7, - copilot-language >= 4.6 && < 4.7, - copilot-libraries >= 4.6 && < 4.7, - copilot-prettyprinter >= 4.6 && < 4.7, + copilot >= 4.6.1 && < 4.7, + copilot-language >= 4.6.1 && < 4.7, + copilot-libraries >= 4.6.1 && < 4.7, + copilot-prettyprinter >= 4.6.1 && < 4.7, copilot-verifier exposed-modules: Copilot.Verifier.Examples From d119ebc92046eade34010753ebfb397ab506d52a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:38 +0000 Subject: [PATCH 292/483] copilot-visualizer: Version bump (4.6.1). Refs #705. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 6afd9070a..c481a8d00 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.6 +version: 4.6.1 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.6 && < 4.7, - copilot-core >= 4.6 && < 4.7, - copilot-interpreter >= 4.6 && < 4.7, - copilot-language >= 4.6 && < 4.7 + copilot >= 4.6.1 && < 4.7, + copilot-core >= 4.6.1 && < 4.7, + copilot-interpreter >= 4.6.1 && < 4.7, + copilot-language >= 4.6.1 && < 4.7 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.6 && < 4.7, + copilot >= 4.6.1 && < 4.7, copilot-visualizer if flag(examples) From 1e0f4f0dbeb1f72fa1cfad59e5ab25e8ea09888e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:42:15 +0000 Subject: [PATCH 293/483] copilot: Version bump (4.6.1). Refs #705. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index d9179eb46..66304a0f8 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.6 +version: 4.6.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.6 && < 4.7 - , copilot-theorem >= 4.6 && < 4.7 - , copilot-language >= 4.6 && < 4.7 - , copilot-libraries >= 4.6 && < 4.7 - , copilot-c99 >= 4.6 && < 4.7 - , copilot-prettyprinter >= 4.6 && < 4.7 + , copilot-core >= 4.6.1 && < 4.7 + , copilot-theorem >= 4.6.1 && < 4.7 + , copilot-language >= 4.6.1 && < 4.7 + , copilot-libraries >= 4.6.1 && < 4.7 + , copilot-c99 >= 4.6.1 && < 4.7 + , copilot-prettyprinter >= 4.6.1 && < 4.7 exposed-modules: Language.Copilot, Language.Copilot.Main From eb078b9b0737e3b63dd1e86120b663d35321622c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 294/483] copilot-core: Document changes in CHANGELOG. Refs #705. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index b0b94a732..c00db088a 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From 86b8eaf38be1665d0f9057477be4dd390d228d87 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 295/483] copilot-interpreter: Document changes in CHANGELOG. Refs #705. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index c6f5b6cbf..1827da72f 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From bf0230aa301e8e7ac53d2549bb78e85a92306340 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 296/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #705. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 26b0500ad..ed0b61ad9 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From f9116e0d85352c495ab80f8b7c419805d2f154e9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 297/483] copilot-bluespec: Document changes in CHANGELOG. Refs #705. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 168d3ac98..9235a2a27 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2025-12-19 +2026-01-07 + * Version bump (4.6.1). (#705) * Disable test suite via newly added manual cabal flag. (#673) * Fix translation of constant floating-point values. (#696) From 7b603c8443a6ffc6e7a065d63fe6c163d8fdbce4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 298/483] copilot-c99: Document changes in CHANGELOG. Refs #705. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 8b7be6866..7a2ef8da6 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From c5ce39c23ffe2b3505b8aa555f89d7a16c98d033 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 299/483] copilot-theorem: Document changes in CHANGELOG. Refs #705. --- copilot-theorem/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 1c12b51d7..f3dd23d68 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ 2026-01-07 + * Version bump (4.6.1). (#705) * Relax version constraint on containers, random. (#603) 2025-11-07 From 4df4f0a79f626bbe6ce57201fcb9ab047db61906 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 300/483] copilot-language: Document changes in CHANGELOG. Refs #705. --- copilot-language/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index d6e10102b..eca064537 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,4 +1,5 @@ 2026-01-07 + * Version bump (4.6.1). (#705) * Relax version constraint on containers. (#603) 2025-11-07 From ac295eab915564213978e62251a6ae8afec1eb12 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 301/483] copilot-libraries: Document changes in CHANGELOG. Refs #705. --- copilot-libraries/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index bc67991ff..6ae010b91 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ 2026-01-07 + * Version bump (4.6.1). (#705) * Relax version constraint on containers. (#603) 2025-11-07 From e650b743bd33490bd2c96d61b18a2491cc05e3f9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 302/483] copilot-verifier: Document changes in CHANGELOG. Refs #705. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index e4b2f9542..ca8740289 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From 2d69e9b62ee33de1672b4f20b3a2806803373239 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 303/483] copilot-visualizer: Document changes in CHANGELOG. Refs #705. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 2dbe2a7ae..85bc41739 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-01-07 + * Version bump (4.6.1). (#705) + 2025-11-07 * Version bump (4.6). (#679) From 62940d9d984fc37a90becfc573e8ff5f2df10019 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 8 Jan 2026 00:47:38 +0000 Subject: [PATCH 304/483] copilot: Document changes in CHANGELOG. Refs #705. --- copilot/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 759fda729..15678af71 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ 2026-01-07 + * Version bump (4.6.1). (#705) * Relax version constraint on optparse-applicative. (#603) 2025-11-07 From ed06e9754abfe88eb827f03921d8caf342ec3ff6 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 14 Oct 2025 13:30:30 -0500 Subject: [PATCH 305/483] copilot-c99: Raise error when compiling empty array or struct. Refs #695. Copilot allows defining empty arrays (i.e., values of type Array 0) and empty structs (i.e., structs with no fields), but using empty arrays or structs in C99 is undefined behavior. Moreover, copilot-c99 will often crash if you attempt to compile a Copilot specification that uses an empty array or struct. This commit raises an appropriate error when attempting to translate a zero-length array or a struct with no fields to C99. Previously, Copilot would either crash with a confusing error or silently translate the offending spec to invalid C99. --- copilot-c99/src/Copilot/Compile/C99/Error.hs | 19 ++++++++++++++++++- copilot-c99/src/Copilot/Compile/C99/Expr.hs | 17 +++++++++++++---- copilot-c99/src/Copilot/Compile/C99/Type.hs | 10 +++++++--- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/copilot-c99/src/Copilot/Compile/C99/Error.hs b/copilot-c99/src/Copilot/Compile/C99/Error.hs index f42c794de..8ab2de608 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Error.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Error.hs @@ -5,7 +5,10 @@ -- -- Custom functions to report error messages to users. module Copilot.Compile.C99.Error - ( impossible ) + ( impossible + , errorEmptyStruct + , errorZeroLengthArray + ) where -- | Report an error due to a bug in Copilot. @@ -18,3 +21,17 @@ impossible function package = ++ ". Please file an issue at " ++ "https://github.com/Copilot-Language/copilot/issues" ++ " or email the maintainers at " + +-- | Report an error when attempting to compile a zero-length array to C99. +-- C99 does not support zero-length arrays, so Copilot cannot compile +-- specifications that use them. +errorZeroLengthArray :: a +errorZeroLengthArray = + error "copilot-c99: Cannot compile zero-length arrays to C99.\n" + +-- | Report an error when attempting to compile an empty struct to C99. +-- C99 does not support empty structs, so Copilot cannot compile +-- specifications that use them. +errorEmptyStruct :: a +errorEmptyStruct = + error "copilot-c99: Cannot compile empty structs to C99.\n" diff --git a/copilot-c99/src/Copilot/Compile/C99/Expr.hs b/copilot-c99/src/Copilot/Compile/C99/Expr.hs index 14e129be9..fec731fbf 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Expr.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Expr.hs @@ -18,7 +18,7 @@ import Copilot.Core ( Array, Expr (..), Field (..), Op1 (..), Op2 (..), arrayElems, toValues, typeLength, typeSize ) -- Internal imports -import Copilot.Compile.C99.Error ( impossible ) +import Copilot.Compile.C99.Error ( impossible, errorEmptyStruct, errorZeroLengthArray ) import Copilot.Compile.C99.Name ( exCpyName, streamAccessorName ) import Copilot.Compile.C99.Type ( transLocalVarDeclType, transType, transTypeName ) @@ -371,14 +371,23 @@ constFieldInit (Value ty (Field val)) = C.InitItem Nothing $ constInit ty val -- | Transform a Copilot Struct, based on the struct fields, into a list of C99 -- initializer values. +-- +-- Raises an error if the struct is empty, as C99 does not support structs with +-- no fields. constStruct :: [Value a] -> NonEmpty.NonEmpty C.InitItem -constStruct val = NonEmpty.fromList $ map constFieldInit val +constStruct val = case val of + [] -> errorEmptyStruct + _ -> NonEmpty.fromList $ map constFieldInit val -- | Transform a Copilot Array, based on the element values and their type, -- into a list of C99 initializer values. +-- +-- Raises an error if the array is empty, as C99 does not support zero-length +-- arrays. constArray :: Type a -> [a] -> NonEmpty.NonEmpty C.InitItem -constArray ty = - NonEmpty.fromList . map (C.InitItem Nothing . constInit ty) +constArray ty xs = case xs of + [] -> errorZeroLengthArray + _ -> NonEmpty.fromList $ map (C.InitItem Nothing . constInit ty) xs -- | Explicitly cast a C99 value to a type. explicitTy :: Type a -> C.Expr -> C.Expr diff --git a/copilot-c99/src/Copilot/Compile/C99/Type.hs b/copilot-c99/src/Copilot/Compile/C99/Type.hs index 78777617b..525fccea9 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Type.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Type.hs @@ -12,7 +12,9 @@ module Copilot.Compile.C99.Type import qualified Language.C99.Simple as C -- Internal imports: Copilot -import Copilot.Core ( Type (..), typeLength, typeName ) +import Copilot.Core ( Struct (..), Type (..), typeLength, + typeName ) +import Copilot.Compile.C99.Error ( errorEmptyStruct, errorZeroLengthArray ) -- | Translate a Copilot type to a C99 type. transType :: Type a -> C.Type @@ -28,10 +30,12 @@ transType ty = case ty of Word64 -> C.TypeSpec $ C.TypedefName "uint64_t" Float -> C.TypeSpec C.Float Double -> C.TypeSpec C.Double - Array ty' -> C.Array (transType ty') len + Array ty' | typeLength ty == 0 -> errorZeroLengthArray + | otherwise -> C.Array (transType ty') len where len = Just $ C.LitInt $ fromIntegral $ typeLength ty - Struct s -> C.TypeSpec $ C.Struct (typeName s) + Struct s | null (toValues s) -> errorEmptyStruct + | otherwise -> C.TypeSpec $ C.Struct (typeName s) -- | Translate a Copilot type to a valid (local) variable declaration C99 type. -- From dc96eacc8caaa07e298d695dbfe8c5b3f3acce01 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Mon, 16 Feb 2026 22:21:14 -0600 Subject: [PATCH 306/483] copilot-c99: Test for error when compiling empty array or struct. Refs #695. Copilot allows defining empty arrays (i.e., values of type Array 0) and empty structs (i.e., structs with no fields), but using empty arrays or structs in C99 is undefined behavior. Moreover, copilot-c99 will often crash if you attempt to compile a Copilot specification that uses an empty array or struct. A prior commit introduced a solution for this problem that makes the C99 backend detect these undesired struct or array types. This commit adds a test to check that attempting to compile specs containing zero-length arrays or empty structs to C99 raises an appropriate error. --- copilot-c99/tests/Test/Copilot/Compile/C99.hs | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/copilot-c99/tests/Test/Copilot/Compile/C99.hs b/copilot-c99/tests/Test/Copilot/Compile/C99.hs index 60276e870..b02fc23ae 100644 --- a/copilot-c99/tests/Test/Copilot/Compile/C99.hs +++ b/copilot-c99/tests/Test/Copilot/Compile/C99.hs @@ -8,10 +8,11 @@ module Test.Copilot.Compile.C99 -- External imports import Control.Arrow ((&&&)) -import Control.Exception (IOException, catch) -import Control.Monad (when) +import Control.Exception (ErrorCall (..), IOException, catch, + try) +import Control.Monad (unless, when) import Data.Bits (Bits, complement) -import Data.List (intercalate) +import Data.List (intercalate, isSubsequenceOf) import Data.Type.Equality (testEquality) import Data.Typeable (Proxy (..), (:~:) (Refl)) import GHC.TypeLits (KnownNat, natVal) @@ -24,7 +25,9 @@ import System.Posix.Temp (mkdtemp) import System.Process (callProcess, readProcess) import System.Random (Random) import Test.Framework (Test, testGroup) +import Test.Framework.Providers.HUnit (testCase) import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.HUnit (Assertion, assertFailure) import Test.QuickCheck (Arbitrary, Gen, Property, arbitrary, choose, elements, forAll, forAllBlind, frequency, @@ -50,6 +53,7 @@ tests = , testProperty "Compile specification in custom dir" testCompileCustomDir , testProperty "Run specification" testRun , testProperty "Run and compare results" testRunCompare + , testCase "Reject empty arrays and structs" testRejectEmpties ] -- * Individual tests @@ -137,6 +141,65 @@ testCompileCustomDir = ioProperty $ do args = [] +data Empty = Empty + +instance Struct Empty where + typeName _ = "" + toValues _ = [] + +instance Typed Empty where + typeOf = Struct Empty + +-- | Test that compiling a spec with an empty array or struct raises an error. +-- C99 does not support zero-length arrays or structs without fields, so Copilot +-- should report an error during compilation. +testRejectEmpties :: Assertion +testRejectEmpties = do + tmpDir <- getTemporaryDirectory + setCurrentDirectory tmpDir + + r1 <- try (compile "copilot_test" $ spec t1) :: IO (Either ErrorCall ()) + unless (isErrZeroArray r1) $ assertFailure "Const zero-length array" + + r2 <- try (compile "copilot_test" $ spec t2) :: IO (Either ErrorCall ()) + unless (isErrZeroArray r2) $ assertFailure "Extern zero-length array" + + r3 <- try (compile "copilot_test" $ spec t3) :: IO (Either ErrorCall ()) + unless (isErrEmptyStruct r3) $ assertFailure "Const empty struct" + + r4 <- try (compile "copilot_test" $ spec t4) :: IO (Either ErrorCall ()) + unless (isErrEmptyStruct r4) $ assertFailure "Extern empty struct" + + where + + spec a = Spec streams observers (triggers a) properties + + streams = [] + observers = [] + triggers a = [ Trigger "trig" (Const Bool True) a ] + properties = [] + + t1 = [ UExpr arrTy (Const arrTy (array [])) ] + t2 = [ UExpr arrTy (ExternVar arrTy "arrs" Nothing) ] + t3 = [ UExpr structTy (Const structTy Empty) ] + t4 = [ UExpr structTy (ExternVar structTy "structs" Nothing) ] + + arrTy :: Type (Array 0 Bool) + arrTy = Array Bool + + structTy :: Type Empty + structTy = Struct Empty + + isErrZeroArray :: Either ErrorCall a -> Bool + isErrZeroArray e = case e of + Left (ErrorCall msg) -> "zero-length array" `isSubsequenceOf` msg + _ -> False + + isErrEmptyStruct :: Either ErrorCall a -> Bool + isErrEmptyStruct e = case e of + Left (ErrorCall msg) -> "empty struct" `isSubsequenceOf` msg + _ -> False + -- | Test compiling a spec and running the resulting program. -- -- The actual behavior is ignored. From a0126958b9899c2c26ae7907aa66a6aad17e6750 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Mon, 16 Feb 2026 22:28:22 -0600 Subject: [PATCH 307/483] copilot-c99: Document changes in CHANGELOG. Refs #695. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 7a2ef8da6..7e5b7d15d 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-02-16 + * Raise error when compiling empty arrays or structs. (#695) + 2026-01-07 * Version bump (4.6.1). (#705) From b1e21ae35dcae6fb9fda63e462ae11bd411cec90 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 22:56:55 -0800 Subject: [PATCH 308/483] copilot-verifier: Relax version constraints on what4. Refs #716. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, ogma-extra has seen release 1.12, but copilot-visualizer needs versions strictly lower than 1.10. This commit relaxes the version constrains on `what4` for the `copilot-verifier` package. --- copilot-verifier/copilot-verifier.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index f77ebfcaf..691216ecc 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -62,7 +62,7 @@ common bldflags text, transformers, vector, - what4 >= 1.6.1 && < 1.7 + what4 >= 1.6.1 && < 1.8 library import: bldflags From e740fdd1e8babf2b1136b603cde4a9361a75b2d4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 22:57:14 -0800 Subject: [PATCH 309/483] copilot-visualizer: Relax version constraints on ogma-extra. Refs #716. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. For example, ogma-extra has seen release 1.12, but copilot-visualizer needs versions strictly lower than 1.10. This commit relaxes the version constrains on `ogma-extra` for the `copilot-visualizer` package. --- copilot-visualizer/copilot-visualizer.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index c481a8d00..a8b3b9511 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -61,7 +61,7 @@ library filepath >= 1.4.2 && < 1.6, hint >= 0.9.0 && < 1.10, pretty >= 1.0 && < 1.2, - ogma-extra >= 1.6.0 && < 1.10, + ogma-extra >= 1.6.0 && < 1.13, text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, From 2fa1c2d93071b941fecd10400b68a36b6a4280e0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 23:01:07 -0800 Subject: [PATCH 310/483] copilot-verifier: Document changes in CHANGELOG. Refs #716. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index ca8740289..ec8c2c738 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Relax version constraints on what4. (#716) + 2026-01-07 * Version bump (4.6.1). (#705) From e3a02223f273ae89b6616fac7c4ac33994346be8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 23:01:40 -0800 Subject: [PATCH 311/483] copilot-visualizer: Document changes in CHANGELOG. Refs #716. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 85bc41739..3bbc45cc2 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Relax version constraints on ogma-extra. (#716) + 2026-01-07 * Version bump (4.6.1). (#705) From de96e47d79e1c26ab0424c159d91e235dac337a7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 312/483] copilot-core: Version bump (4.7). Refs #714. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 336db0f26..6d99e77d6 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.6.1 +version: 4.7 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From ad0d904a089ac7c5c63d09aead59aab32d3adbda Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 313/483] copilot-bluespec: Version bump (4.7). Refs #714. --- copilot-bluespec/copilot-bluespec.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index b727af7fa..3ac0839cf 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.6.1 +version : 4.7 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,8 +51,8 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.6.1 && < 4.7 - , language-bluespec >= 0.1 && < 0.2 + , copilot-core >= 4.7 && < 4.8 + , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From fb6b1821826bd8f961af141cc391f779f7efcfe8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 314/483] copilot-c99: Version bump (4.7). Refs #714. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 40407cec2..0567e9416 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.6.1 +version : 4.7 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.6.1 && < 4.7 + , copilot-core >= 4.7 && < 4.8 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From 7710ae216956eb372e9a3d667a39d5ac32ffd1e8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 315/483] copilot-prettyprinter: Version bump (4.7). Refs #714. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index b303db95f..f6f5fcc57 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.6.1 +version: 4.7 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.6.1 && < 4.7 + copilot-core >= 4.7 && < 4.8 exposed-modules: From 750f585d516dfec7bb1ae25c26ce0d3cfe196595 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 316/483] copilot-interpreter: Version bump (4.7). Refs #714. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 7395226ab..9ef09f117 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.6.1 +version: 4.7 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.6.1 && < 4.7 + copilot-core >= 4.7 && < 4.8 exposed-modules: From 41194365d25a1eac86a3e8cbfdd563f85084dae7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 317/483] copilot-theorem: Version bump (4.7). Refs #714. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 173e90eca..f8b4ff57e 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.6.1 +version : 4.7 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -62,8 +62,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.6.1 && < 4.7 - , copilot-prettyprinter >= 4.6.1 && < 4.7 + , copilot-core >= 4.7 && < 4.8 + , copilot-prettyprinter >= 4.7 && < 4.8 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 65ef8e6332b2f5bf2ccb2ccbc318ddfda416b060 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 318/483] copilot-language: Version bump (4.7). Refs #714. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 0b65b5798..740bb2b8c 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.6.1 +version: 4.7 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.6.1 && < 4.7 - , copilot-interpreter >= 4.6.1 && < 4.7 - , copilot-theorem >= 4.6.1 && < 4.7 + , copilot-core >= 4.7 && < 4.8 + , copilot-interpreter >= 4.7 && < 4.8 + , copilot-theorem >= 4.7 && < 4.8 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From 0133c62e635713ec46f19cc04a7d42f6b798128e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 319/483] copilot-libraries: Version bump (4.7). Refs #714. --- copilot-libraries/copilot-libraries.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index c864c9cda..e194daf78 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.6.1 +version: 4.7 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -38,10 +38,10 @@ library build-depends: base >= 4.9 && < 5 - , containers >= 0.4 && < 0.9 - , mtl >= 2.0 && < 2.4 - , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.6.1 && < 4.7 + , containers >= 0.4 && < 0.9 + , mtl >= 2.0 && < 2.4 + , parsec >= 2.0 && < 3.2 + , copilot-language >= 4.7 && < 4.8 exposed-modules: Copilot.Library.Libraries From 3d9d882fcd9922aa4880d02bfc7b70c36a8cfa41 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 320/483] copilot-verifier: Version bump (4.7). Refs #714. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 691216ecc..c7bf36a10 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.6.1 +Version: 4.7 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.6.1 && < 4.7, - copilot-core >= 4.6.1 && < 4.7, - copilot-theorem >= 4.6.1 && < 4.7, + copilot-c99 >= 4.7 && < 4.8, + copilot-core >= 4.7 && < 4.8, + copilot-theorem >= 4.7 && < 4.8, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.6.1 && < 4.7, - copilot-language >= 4.6.1 && < 4.7, - copilot-libraries >= 4.6.1 && < 4.7, - copilot-prettyprinter >= 4.6.1 && < 4.7, + copilot >= 4.7 && < 4.8, + copilot-language >= 4.7 && < 4.8, + copilot-libraries >= 4.7 && < 4.8, + copilot-prettyprinter >= 4.7 && < 4.8, copilot-verifier exposed-modules: Copilot.Verifier.Examples From c59f73ce7f29c6ab7e420b65f09b0fb41ce47110 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 321/483] copilot-visualizer: Version bump (4.7). Refs #714. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index a8b3b9511..0500c1b43 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.6.1 +version: 4.7 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.6.1 && < 4.7, - copilot-core >= 4.6.1 && < 4.7, - copilot-interpreter >= 4.6.1 && < 4.7, - copilot-language >= 4.6.1 && < 4.7 + copilot >= 4.7 && < 4.8, + copilot-core >= 4.7 && < 4.8, + copilot-interpreter >= 4.7 && < 4.8, + copilot-language >= 4.7 && < 4.8 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.6.1 && < 4.7, + copilot >= 4.7 && < 4.8, copilot-visualizer if flag(examples) From ff624408e746be0de496c4bd8b95d8976b909744 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 19:59:22 -0800 Subject: [PATCH 322/483] copilot: Version bump (4.7). Refs #714. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 66304a0f8..68ff811ec 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.6.1 +version: 4.7 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.6.1 && < 4.7 - , copilot-theorem >= 4.6.1 && < 4.7 - , copilot-language >= 4.6.1 && < 4.7 - , copilot-libraries >= 4.6.1 && < 4.7 - , copilot-c99 >= 4.6.1 && < 4.7 - , copilot-prettyprinter >= 4.6.1 && < 4.7 + , copilot-core >= 4.7 && < 4.8 + , copilot-theorem >= 4.7 && < 4.8 + , copilot-language >= 4.7 && < 4.8 + , copilot-libraries >= 4.7 && < 4.8 + , copilot-c99 >= 4.7 && < 4.8 + , copilot-prettyprinter >= 4.7 && < 4.8 exposed-modules: Language.Copilot, Language.Copilot.Main From 58a46b16c9aa34e452f2b5e1d732aff17aa8f87f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 323/483] copilot-core: Document changes in CHANGELOG. Refs #714. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index c00db088a..671e39ef6 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) From 1da71446819d71af33b469a9d1475500e3125231 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 324/483] copilot-bluespec: Document changes in CHANGELOG. Refs #714. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 9235a2a27..9072a0a43 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) * Disable test suite via newly added manual cabal flag. (#673) From 186daf5724c48f38100d9efbaaf5dd1dbafa816f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 325/483] copilot-c99: Document changes in CHANGELOG. Refs #714. --- copilot-c99/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 7e5b7d15d..856d51497 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,4 +1,5 @@ -2026-02-16 +2026-03-07 + * Version bump (4.7). (#714) * Raise error when compiling empty arrays or structs. (#695) 2026-01-07 From 3149bcc1ab290f0281fc23bb23367aec974793fd Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 326/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #714. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index ed0b61ad9..efa6ccf51 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) From e5b1bc27c302d663b5ebb94e2f76ab675b6311df Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 327/483] copilot-interpreter: Document changes in CHANGELOG. Refs #714. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 1827da72f..c2920d179 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) From 0ee707328bf1b4996208af713931b08a503fc913 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 328/483] copilot-theorem: Document changes in CHANGELOG. Refs #714. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index f3dd23d68..4aaf2cf97 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) * Relax version constraint on containers, random. (#603) From 680467e4a8a74a33c169b421801ddbe5b23f6259 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 329/483] copilot-language: Document changes in CHANGELOG. Refs #714. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index eca064537..4751f7061 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) * Relax version constraint on containers. (#603) From 9d3605a597bcdf130d3f9ec291681d21a2dcc2a5 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 330/483] copilot-libraries: Document changes in CHANGELOG. Refs #714. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 6ae010b91..171b9b7d7 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) * Relax version constraint on containers. (#603) From 7d651deafe18dec7f88516040e91841f16df010a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 331/483] copilot-verifier: Document changes in CHANGELOG. Refs #714. --- copilot-verifier/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index ec8c2c738..049ec2243 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,4 +1,5 @@ 2026-03-07 + * Version bump (4.7). (#714) * Relax version constraints on what4. (#716) 2026-01-07 From b4f0c279c1a6b5f288f865d830303fd37ca7a58f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 332/483] copilot-visualizer: Document changes in CHANGELOG. Refs #714. --- copilot-visualizer/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 3bbc45cc2..402ec34d5 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,4 +1,5 @@ 2026-03-07 + * Version bump (4.7). (#714) * Relax version constraints on ogma-extra. (#716) 2026-01-07 From cdbcdac2f56db48cac22ee0ef225b0396c2a4929 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Mar 2026 20:00:57 -0800 Subject: [PATCH 333/483] copilot: Document changes in CHANGELOG. Refs #714. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 15678af71..dd34bc70f 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-07 + * Version bump (4.7). (#714) + 2026-01-07 * Version bump (4.6.1). (#705) * Relax version constraint on optparse-applicative. (#603) From 9743faef83ea8d04722c1a215410b71968ab3340 Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Fri, 19 Dec 2025 12:06:37 -0500 Subject: [PATCH 334/483] copilot-bluespec: Fix translation of special Float values to Bluespec. Refs #697. `copilot-bluespec`'s translation from Copilot to Bluespec up-casts `Float` values to `Double` values using the `realToFrac` function. `realToFrac` incorrectly handles special floating-point values such as negative zero, infinity, and NaN values, causing `copilot-bluespec` to generate the wrong `Float` values on the Bluespec end. This commit removes the use of `realToFrac` in favor of an alternative approach based on `GHC.Float.float2Double`, which correctly handles most special floating-point values. A notable exception is NaN values, as `float2Double` does not reliably preserve the payload of a NaN value. As such, we include a special case for NaN values that takes care to preserve payloads. Co-authored-by: Ryan Scott --- .../src/Copilot/Compile/Bluespec/Expr.hs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs index 39ff1ad22..2b71b3957 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs @@ -18,7 +18,7 @@ import Data.String (IsString (..)) import qualified Language.Bluespec.Classic.AST as BS import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS import Numeric.Floating.IEEE.NaN (isSignaling, setPayloadSignaling, setPayload, getPayload) -import GHC.Float (double2Float, castFloatToWord32, castDoubleToWord64) +import GHC.Float (double2Float, castFloatToWord32, castDoubleToWord64, float2Double) -- Internal imports: Copilot import Copilot.Core @@ -535,7 +535,7 @@ constTy ty = Word16 -> constInt ty . toInteger Word32 -> constInt ty . toInteger Word64 -> constInt ty . toInteger - Float -> constFP ty . realToFrac + Float -> constFP ty . nanSafeFloat2Double Double -> constFP ty -- Translating a Copilot array literal to a Bluespec Vector is somewhat @@ -563,6 +563,20 @@ constTy ty = , constTy ty'' val )) (toValues v)) + where + -- Convert a Float to a Double. This function takes care to preserve + -- special floating-point values, such as negative zero, infinity, and NaN + -- values. + nanSafeFloat2Double :: Float -> Double + nanSafeFloat2Double x + -- float2Double does not preserve the payloads of NaN values, so we + -- include special cases for translating NaNs. + | isNaN x && isSignaling x = setPayloadSignaling payload + | isNaN x = setPayload payload + | otherwise = float2Double x + where + payload :: Double + payload = float2Double $ getPayload x -- | Transform a list of Copilot Core expressions of a given 'Type' into a -- Bluespec @Vector@ expression. From caafd778b32a0ca2ed6638daee8e008f403a6261 Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Fri, 19 Dec 2025 14:23:42 -0500 Subject: [PATCH 335/483] copilot-theorem: Handle special Float values correctly for counterexamples. Refs #697. `copilot-theorem`'s counterexample-reporting machinery up-casts `Float` values to `Double` values using the `realToFrac` function. `realToFrac` incorrectly handles special floating-point values such as negative zero, infinity, and NaN values, causing `copilot-theorem` to generate incorrect counterexamples when these special values are involved. This commit removes the use of `realToFrac` in favor of an alternative approach based on `GHC.Float.double2Float`, which correctly handles most special floating-point values. A notable exception is NaN values, as `double2Float` does not reliably preserve the payload of a NaN value. As such, we include a special case for NaN values that takes care to preserve payloads. Co-authored-by: Ryan Scott --- copilot-theorem/copilot-theorem.cabal | 1 + copilot-theorem/src/Copilot/Theorem/What4.hs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index f8b4ff57e..87f53807f 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -51,6 +51,7 @@ library , containers >= 0.4 && < 0.9 , data-default >= 0.7 && < 0.9 , directory >= 1.3 && < 1.4 + , fp-ieee >= 0.1 && < 0.2 , libBF >= 0.6.2 && < 0.7 , mtl >= 2.0 && < 2.4 , panic >= 0.4.0 && < 0.5 diff --git a/copilot-theorem/src/Copilot/Theorem/What4.hs b/copilot-theorem/src/Copilot/Theorem/What4.hs index 51ed6e8ff..97e55a852 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4.hs @@ -77,8 +77,9 @@ import Data.Parameterized.NatRepr import Data.Parameterized.Nonce import Data.Parameterized.Some import qualified Data.Parameterized.Vector as V -import GHC.Float (castWord32ToFloat, castWord64ToDouble) +import GHC.Float (castWord32ToFloat, castWord64ToDouble, double2Float) import LibBF (BigFloat, bfToDouble, pattern NearEven) +import Numeric.Floating.IEEE.NaN (getPayload, isSignaling, setPayload, setPayloadSignaling) import qualified Panic as Panic import Copilot.Theorem.What4.Translate @@ -707,7 +708,7 @@ valFromExpr ge xe = case xe of XFloat e -> Some . CopilotValue CT.Float <$> iFloatGroundEval WFP.SingleFloatRepr e - (realToFrac . fst . bfToDouble NearEven) + (nanSafeDoubleToFloat . fst . bfToDouble NearEven) fromRational (castWord32ToFloat . fromInteger . BV.asUnsigned) XDouble e -> @@ -757,3 +758,17 @@ valFromExpr ge xe = case xe of WB.FloatIEEERepr -> ieeeK <$> WG.groundEval ge e WB.FloatRealRepr -> realK <$> WG.groundEval ge e WB.FloatUninterpretedRepr -> uninterpK <$> WG.groundEval ge e + + -- Convert a Double to a Float. This function takes care to preserve + -- special floating-point values, such as negative zero, infinity, and NaN + -- values. + nanSafeDoubleToFloat :: Double -> Float + nanSafeDoubleToFloat x + -- double2Float does not preserve the payloads of NaN values, so we + -- include a special case for translating NaNs. + | isNaN x && isSignaling x = setPayloadSignaling payload + | isNaN x = setPayload payload + | otherwise = double2Float x + where + payload :: Float + payload = double2Float $ getPayload x From b2d81f6b64ffcb6a0e5e445736c7a8aae36f1f5d Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Tue, 31 Mar 2026 09:53:19 -0400 Subject: [PATCH 336/483] copilot-bluespec: Document changes in CHANGELOG. Refs #697. Co-authored-by: Ryan Scott --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 9072a0a43..c0ab51bc1 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-31 + * Fix translation of special Float values to Bluespec. (#697) + 2026-03-07 * Version bump (4.7). (#714) From 6e2fed247bbf8b1a878306b8c0227c3629266ffb Mon Sep 17 00:00:00 2001 From: Trevor Kann Date: Tue, 31 Mar 2026 09:56:01 -0400 Subject: [PATCH 337/483] copilot-theorem: Document changes in CHANGELOG. Refs #697. Co-authored-by: Ryan Scott --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 4aaf2cf97..2f521a853 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-03-31 + * Handle special Float values correctly for counterexamples. (#697) + 2026-03-07 * Version bump (4.7). (#714) From c854b5ff70a1ef99bf34137a186fe63a6ad5eb6a Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 31 Mar 2026 11:46:52 -0500 Subject: [PATCH 338/483] copilot-theorem: Update examples to compile with copilot-theorem >= 3.0. Refs #692. Some example programs in the copilot-theorem/example subdirectory have bitrotted, as they depend on a Copilot.Theorem.Prover.Z3 module that is no longer included in copilot-theorem.cabal (as of 6a67d89). Attempting to build any of these examples results in compilation failures. This commit fixes the examples so they will compile using the current version of copilot-theorem. --- copilot-theorem/examples/BoyerMoore.hs | 4 ++-- copilot-theorem/examples/Grey.hs | 6 +++--- copilot-theorem/examples/Incr.hs | 4 ++-- copilot-theorem/examples/SerialBoyerMoore.hs | 6 +++--- copilot-theorem/examples/SphericalWCV.hs | 6 +++--- copilot-theorem/examples/WCV.hs | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/copilot-theorem/examples/BoyerMoore.hs b/copilot-theorem/examples/BoyerMoore.hs index d4cebf638..47fe790af 100644 --- a/copilot-theorem/examples/BoyerMoore.hs +++ b/copilot-theorem/examples/BoyerMoore.hs @@ -5,7 +5,7 @@ module BoyerMoore where import Copilot.Language hiding (length) import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (debug, def, induction, z3) import Copilot.Core.Type @@ -71,7 +71,7 @@ spec = do maj = majorityVote ss induct :: Proof Universal -induct = induction def { nraNLSat = False, debug = False } +induct = induction def { debug = False } z3 -- | Initial value for a given type. -- diff --git a/copilot-theorem/examples/Grey.hs b/copilot-theorem/examples/Grey.hs index 9a1ca4319..91500ba1a 100644 --- a/copilot-theorem/examples/Grey.hs +++ b/copilot-theorem/examples/Grey.hs @@ -4,7 +4,7 @@ module Grey where import Copilot.Language import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (Options (..), def, induction, kInduction, z3) import Prelude () import Data.String (fromString) @@ -32,7 +32,7 @@ spec = do r = extern "reset" Nothing induct :: Proof Universal -induct = induction def { nraNLSat = False, debug = False } +induct = induction def { debug = False } z3 kinduct :: Word32 -> Proof Universal -kinduct k = kInduction def { nraNLSat = False, startK = k, maxK = k, debug = False } +kinduct k = kInduction def { startK = k, maxK = k, debug = False } z3 diff --git a/copilot-theorem/examples/Incr.hs b/copilot-theorem/examples/Incr.hs index 3c39a008d..dd6ccdc42 100644 --- a/copilot-theorem/examples/Incr.hs +++ b/copilot-theorem/examples/Incr.hs @@ -4,7 +4,7 @@ import Prelude () import Copilot.Language import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (debug, def, induction, z3) spec = do bounds <- prop "bounds" (forAll $ x < 255) @@ -16,4 +16,4 @@ spec = do x = [2] ++ (1 + x) induct :: Proof Universal -induct = induction def { nraNLSat = False, debug = True } +induct = induction def { debug = True } z3 diff --git a/copilot-theorem/examples/SerialBoyerMoore.hs b/copilot-theorem/examples/SerialBoyerMoore.hs index 5f0d42092..eb858a6a6 100644 --- a/copilot-theorem/examples/SerialBoyerMoore.hs +++ b/copilot-theorem/examples/SerialBoyerMoore.hs @@ -4,7 +4,7 @@ module SerialBoyerMoore where import Copilot.Language import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (Options (..), def, induction, kInduction, z3) import Prelude () import Data.String (fromString) @@ -56,7 +56,7 @@ spec = do (p, s, j) = majority input induct :: Proof Universal -induct = induction def { nraNLSat = False, debug = False } +induct = induction def { debug = False } z3 kinduct :: Word32 -> Proof Universal -kinduct k = kInduction def { nraNLSat = False, startK = k, maxK = k, debug = False } +kinduct k = kInduction def { startK = k, maxK = k, debug = False } z3 diff --git a/copilot-theorem/examples/SphericalWCV.hs b/copilot-theorem/examples/SphericalWCV.hs index 74b899583..06d203adf 100644 --- a/copilot-theorem/examples/SphericalWCV.hs +++ b/copilot-theorem/examples/SphericalWCV.hs @@ -8,7 +8,7 @@ import Prelude () import Copilot.Language import Copilot.Language.Reify import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (debug, def, onlySat, onlyValidity, z3) import qualified Copilot.Language.Operators.Propositional as P @@ -241,7 +241,7 @@ localConvexity = do theorem "6" (P.not (forAll $ locallyConvex tau)) arithSat arith :: Proof Universal -arith = onlyValidity def { nraNLSat = True, debug = False } +arith = onlyValidity def { debug = False } z3 arithSat :: Proof Existential -arithSat = onlySat def { nraNLSat = True, debug = False } +arithSat = onlySat def { debug = False } z3 diff --git a/copilot-theorem/examples/WCV.hs b/copilot-theorem/examples/WCV.hs index 62c27f287..c0f267c68 100644 --- a/copilot-theorem/examples/WCV.hs +++ b/copilot-theorem/examples/WCV.hs @@ -5,7 +5,7 @@ import Prelude () import Copilot.Language import Copilot.Language.Reify import Copilot.Theorem -import Copilot.Theorem.Prover.Z3 +import Copilot.Theorem.Prover.SMT (debug, def, onlySat, onlyValidity, z3) import qualified Copilot.Language.Operators.Propositional as P @@ -165,7 +165,7 @@ localConvexity = do theorem "6" (P.not (forAll $ locallyConvex tau)) arithSat arith :: Proof Universal -arith = onlyValidity def { nraNLSat = True, debug = False } +arith = onlyValidity def { debug = False } z3 arithSat :: Proof Existential -arithSat = onlySat def { nraNLSat = True, debug = False } +arithSat = onlySat def { debug = False } z3 From 579b124a56146f563e620ca9df5d9b1c70640a84 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 31 Mar 2026 12:31:53 -0500 Subject: [PATCH 339/483] copilot-theorem: Document changes in CHANGELOG. Refs #692. --- copilot-theorem/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 2f521a853..909643531 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,5 +1,6 @@ 2026-03-31 * Handle special Float values correctly for counterexamples. (#697) + * Update examples to compile with copilot-theorem >= 3.0. (#692) 2026-03-07 * Version bump (4.7). (#714) From a4126bbed39662575daf0fd4ecdca80ce7476ffb Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 18 May 2025 04:45:39 +0000 Subject: [PATCH 340/483] copilot-libraries: Add stream analysis module. Refs #726. There is a need to have in Copilot a module that includes stream and spec analysis functions. Such functions have been useful in other projects, and they could be added as part of Copilot's standard library so that they are available to all users. This commit adds a new module that provides functions to determine if a stream is always true or always false. --- copilot-libraries/copilot-libraries.cabal | 3 ++- .../src/Copilot/Library/Analysis.hs | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 copilot-libraries/src/Copilot/Library/Analysis.hs diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index e194daf78..145887314 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -44,7 +44,8 @@ library , copilot-language >= 4.7 && < 4.8 exposed-modules: - Copilot.Library.Libraries + Copilot.Library.Analysis + , Copilot.Library.Libraries , Copilot.Library.Clocks , Copilot.Library.LTL , Copilot.Library.PTLTL diff --git a/copilot-libraries/src/Copilot/Library/Analysis.hs b/copilot-libraries/src/Copilot/Library/Analysis.hs new file mode 100644 index 000000000..240addd7c --- /dev/null +++ b/copilot-libraries/src/Copilot/Library/Analysis.hs @@ -0,0 +1,23 @@ +-- | Formally analyze streams. +module Copilot.Library.Analysis + ( triviallyTrue + , triviallyFalse + ) + where + +-- External imports. +import Prelude hiding (not) + +-- Internal imports. +import Copilot.Language (Stream, forAll, not) +import Copilot.Language.Spec (Prop, Universal) + +-- | Property that captures whether a stream is trivially true, meaning that +-- there is no way to make it false. +triviallyTrue :: Stream Bool -> Prop Universal +triviallyTrue = forAll + +-- | Property that captures whether a stream is trivially false, meaning that +-- there is no way to make it true. +triviallyFalse :: Stream Bool -> Prop Universal +triviallyFalse = triviallyTrue . not From fb249d239ed3722b1cbe03e2c6be86e112a2afb4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 20:28:02 -0700 Subject: [PATCH 341/483] copilot-libraries: Document changes in CHANGELOG. Refs #726. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 171b9b7d7..49779ea56 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Add stream analysis module. (#726) + 2026-03-07 * Version bump (4.7). (#714) From 4b5225ee46f9d1ab2c832a043c5ccc7b73c5377f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:55 -0700 Subject: [PATCH 342/483] copilot-libraries: Add state machine module. Refs #728. There is a need to have in Copilot a module that implements state machines. Such functions have been useful in other projects, and they could be added as part of Copilot's standard library so that they are available to all users. This commit adds a state machine module that enables simulating a state machine using simple notation to describe the state machine. --- copilot-libraries/copilot-libraries.cabal | 1 + .../src/Copilot/Library/StateMachines.hs | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 copilot-libraries/src/Copilot/Library/StateMachines.hs diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 145887314..e59ff32f8 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -49,6 +49,7 @@ library , Copilot.Library.Clocks , Copilot.Library.LTL , Copilot.Library.PTLTL + , Copilot.Library.StateMachines , Copilot.Library.Statistics , Copilot.Library.RegExp , Copilot.Library.Utils diff --git a/copilot-libraries/src/Copilot/Library/StateMachines.hs b/copilot-libraries/src/Copilot/Library/StateMachines.hs new file mode 100644 index 000000000..37121b1a0 --- /dev/null +++ b/copilot-libraries/src/Copilot/Library/StateMachines.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- | Simulate state machines using streams. +module Copilot.Library.StateMachines where + +import Copilot.Language (Stream, Typed, constant, ifThenElse, (&&), (++), (==)) +import Prelude hiding ((&&), (++), (==)) + +-- | A definition of a state machine where some elements are defined +-- as streams. +-- +-- A state machine is defined by an initial state, a final state, a no +-- transition stream (true when tere is no input coming in), a list of +-- transitions, and a bad state. +type StateMachine a = (a, a, Stream Bool, [(a, Stream Bool, a)], a) + +-- | Produce a stream that, at any given time, contains the current state of +-- the state machine. +stateMachine :: forall a . (Eq a, Typed a) => StateMachine a -> Stream a +stateMachine (initial, final, noInputData, transitions, bad) = state + where + state = ifThenElses transitions + previousState = [initial] ++ state + + ifThenElses :: [(a, Stream Bool, a)] -> Stream a + ifThenElses [] = + ifThenElse (previousState == constant final && noInputData) + (constant final) + (constant bad) + + ifThenElses ((s1, i, s2):ss) = + ifThenElse + (previousState == constant s1 && i) + (constant s2) + (ifThenElses ss) + +-- | Produce a stream that, at any given time, contains the current state of +-- the state machine as the numeric representation of an enum. +stateMachineEnum :: (Eq b, Typed b, Num b, Enum a) + => StateMachine a + -> Stream b +stateMachineEnum (initial, final, noInputData, transitions, bad) = + stateMachine (fe initial, fe final, noInputData, transitionsE, fe bad) + where + transitionsE = map (\(s1, t, s2) -> (fe s1, t, fe s2)) transitions + fe = fromIntegral . fromEnum From 32704d9c8ebb7bbed0a8c7d4a5bc9aeb15554079 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:55 -0700 Subject: [PATCH 343/483] copilot-libraries: Document changes in CHANGELOG. Refs #728. --- copilot-libraries/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 49779ea56..745bb1b6b 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,5 +1,6 @@ 2026-05-07 * Add stream analysis module. (#726) + * Add state machine module. (#728) 2026-03-07 * Version bump (4.7). (#714) From 2d3d84540473667029e696be9b42c19c2fe1d565 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 344/483] copilot-core: Version bump (4.7.1). Refs #730. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 6d99e77d6..ee463977c 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.7 +version: 4.7.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From 8cb85bbdd90e983572e673de469de4673d46f3dc Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 345/483] copilot-bluespec: Version bump (4.7.1). Refs #730. --- copilot-bluespec/copilot-bluespec.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 3ac0839cf..ffaadaca2 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.7 +version : 4.7.1 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,8 +51,8 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.7 && < 4.8 - , language-bluespec >= 0.1 && < 0.2 + , copilot-core >= 4.7.1 && < 4.8 + , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From 7818e6c1404f62e3bcff10eca35983320746758e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 346/483] copilot-c99: Version bump (4.7.1). Refs #730. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index 0567e9416..d9802bd42 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.7 +version : 4.7.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.7 && < 4.8 + , copilot-core >= 4.7.1 && < 4.8 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From 4b1fa514a3a6f3185fdda5bcaacbd6cecb609478 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 347/483] copilot-prettyprinter: Version bump (4.7.1). Refs #730. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index f6f5fcc57..c40511c43 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.7 +version: 4.7.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.7 && < 4.8 + copilot-core >= 4.7.1 && < 4.8 exposed-modules: From 7752221d3e0be7414bf915bd2bbc401464acffef Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 348/483] copilot-interpreter: Version bump (4.7.1). Refs #730. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 9ef09f117..0303bf17b 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.7 +version: 4.7.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.7 && < 4.8 + copilot-core >= 4.7.1 && < 4.8 exposed-modules: From 02defe133e9e2ce434b542eeedd4e7ba01e543e4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 349/483] copilot-theorem: Version bump (4.7.1). Refs #730. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 87f53807f..cd9d2e00e 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.7 +version : 4.7.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.7 && < 4.8 - , copilot-prettyprinter >= 4.7 && < 4.8 + , copilot-core >= 4.7.1 && < 4.8 + , copilot-prettyprinter >= 4.7.1 && < 4.8 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 8eac0cac7a0bb24082f0a137c7da0ea5845889ce Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 350/483] copilot-language: Version bump (4.7.1). Refs #730. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 740bb2b8c..2cfab8eb8 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.7 +version: 4.7.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.7 && < 4.8 - , copilot-interpreter >= 4.7 && < 4.8 - , copilot-theorem >= 4.7 && < 4.8 + , copilot-core >= 4.7.1 && < 4.8 + , copilot-interpreter >= 4.7.1 && < 4.8 + , copilot-theorem >= 4.7.1 && < 4.8 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From dad3ad411fce1d1db711fb9be0ffff7722557d94 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 351/483] copilot-libraries: Version bump (4.7.1). Refs #730. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index e59ff32f8..85b117f63 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.7 +version: 4.7.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.7 && < 4.8 + , copilot-language >= 4.7.1 && < 4.8 exposed-modules: Copilot.Library.Analysis From 9af5f1194a388f79e2a80b73e660b7b518ab2461 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 352/483] copilot-verifier: Version bump (4.7.1). Refs #730. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index c7bf36a10..339892ec9 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.7 +Version: 4.7.1 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.7 && < 4.8, - copilot-core >= 4.7 && < 4.8, - copilot-theorem >= 4.7 && < 4.8, + copilot-c99 >= 4.7.1 && < 4.8, + copilot-core >= 4.7.1 && < 4.8, + copilot-theorem >= 4.7.1 && < 4.8, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.7 && < 4.8, - copilot-language >= 4.7 && < 4.8, - copilot-libraries >= 4.7 && < 4.8, - copilot-prettyprinter >= 4.7 && < 4.8, + copilot >= 4.7.1 && < 4.8, + copilot-language >= 4.7.1 && < 4.8, + copilot-libraries >= 4.7.1 && < 4.8, + copilot-prettyprinter >= 4.7.1 && < 4.8, copilot-verifier exposed-modules: Copilot.Verifier.Examples From a932329dec110e8d0ed00893c33b649f879df311 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 353/483] copilot-visualizer: Version bump (4.7.1). Refs #730. --- copilot-visualizer/copilot-visualizer.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 0500c1b43..5e8735ea4 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.7 +version: 4.7.1 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.7 && < 4.8, - copilot-core >= 4.7 && < 4.8, - copilot-interpreter >= 4.7 && < 4.8, - copilot-language >= 4.7 && < 4.8 + copilot >= 4.7.1 && < 4.8, + copilot-core >= 4.7.1 && < 4.8, + copilot-interpreter >= 4.7.1 && < 4.8, + copilot-language >= 4.7.1 && < 4.8 exposed-modules: From 4df77d15f74ea237c08e957ab52ec3f954cbdaed Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 354/483] copilot: Version bump (4.7.1). Refs #730. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 68ff811ec..ace61023d 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.7 +version: 4.7.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.7 && < 4.8 - , copilot-theorem >= 4.7 && < 4.8 - , copilot-language >= 4.7 && < 4.8 - , copilot-libraries >= 4.7 && < 4.8 - , copilot-c99 >= 4.7 && < 4.8 - , copilot-prettyprinter >= 4.7 && < 4.8 + , copilot-core >= 4.7.1 && < 4.8 + , copilot-theorem >= 4.7.1 && < 4.8 + , copilot-language >= 4.7.1 && < 4.8 + , copilot-libraries >= 4.7.1 && < 4.8 + , copilot-c99 >= 4.7.1 && < 4.8 + , copilot-prettyprinter >= 4.7.1 && < 4.8 exposed-modules: Language.Copilot, Language.Copilot.Main From df290fb3f4945c3b8a0283d612f9299ff4f84007 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 355/483] copilot-core: Document changes in CHANGELOG. Refs #730. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 671e39ef6..4a019a359 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) From 4c5a53a6c53882fe57f3efb1dd6b8095abb7fbf7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 356/483] copilot-bluespec: Document changes in CHANGELOG. Refs #730. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index c0ab51bc1..63ea6878b 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2026-03-31 +2026-05-07 + * Version bump (4.7.1). (#730) * Fix translation of special Float values to Bluespec. (#697) 2026-03-07 From 77b3a64b58554751393eeb200f467a495e41a922 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 357/483] copilot-c99: Document changes in CHANGELOG. Refs #730. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 856d51497..74c6c1dd4 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) * Raise error when compiling empty arrays or structs. (#695) From d1943d58127e39f62a365065f877adf34b3a3b12 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 358/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #730. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index efa6ccf51..a8e6d3e12 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) From 00b1fa10a39c967935844275ad054f0baa8b034b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 359/483] copilot-interpreter: Document changes in CHANGELOG. Refs #730. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index c2920d179..53984488e 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) From c09bb16c44ea425544192f21d88225b9f1339711 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 360/483] copilot-theorem: Document changes in CHANGELOG. Refs #730. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 909643531..03facaf36 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2026-03-31 +2026-05-07 + * Version bump (4.7.1). (#730) * Handle special Float values correctly for counterexamples. (#697) * Update examples to compile with copilot-theorem >= 3.0. (#692) From d1547de8018afe6469b6b3afd0d15e65ea32c1b2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 361/483] copilot-language: Document changes in CHANGELOG. Refs #730. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 4751f7061..069d20f5d 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) From 3b6e1b2d68177346ce6af788dbc82bc9fd60b3e6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 362/483] copilot-libraries: Document changes in CHANGELOG. Refs #730. --- copilot-libraries/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 745bb1b6b..19b510a63 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ 2026-05-07 + * Version bump (4.7.1). (#730) * Add stream analysis module. (#726) * Add state machine module. (#728) From 2c11c9b25101bd57a7b317eaf9d9ae5b8190905c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 363/483] copilot-verifier: Document changes in CHANGELOG. Refs #730. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 049ec2243..a2b4dd530 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) * Relax version constraints on what4. (#716) From 9921f52eacd65f310438cef2f170f01ae42bb1f6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 364/483] copilot-visualizer: Document changes in CHANGELOG. Refs #730. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 402ec34d5..0735d51ab 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) * Relax version constraints on ogma-extra. (#716) From fce6bdfb3104aadd8af9eb978dfb2b9a75b82aca Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:48 -0700 Subject: [PATCH 365/483] copilot: Document changes in CHANGELOG. Refs #730. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index dd34bc70f..f58dd30e6 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-05-07 + * Version bump (4.7.1). (#730) + 2026-03-07 * Version bump (4.7). (#714) From 5de4a7bb82e9671b7961b8b0b4c222e0738a1bc3 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 9 Jun 2026 13:53:43 -0500 Subject: [PATCH 366/483] copilot-theorem: Extend range of versions of Kind2. Refs #734. In order to keep Copilot effectively working in the current software ecosystem, we need to extend the versions of dependencies that Copilot can be installed with. kind2 has seen release 2.3.0, but copilot-theorem requires kind2 0.7.2. This commit updates the copilot-theorem Kind2 native-format backend to produce output targeting modern releases of Kind2. It has been tested with Kind2 v2.3, the latest at the time of this commit. --- copilot-theorem/copilot-theorem.cabal | 2 +- .../src/Copilot/Theorem/Kind2/AST.hs | 51 +++++-- .../src/Copilot/Theorem/Kind2/Output.hs | 117 +++++++++------ .../src/Copilot/Theorem/Kind2/PrettyPrint.hs | 49 ++++-- .../src/Copilot/Theorem/Kind2/Prover.hs | 16 +- .../src/Copilot/Theorem/Kind2/Translate.hs | 140 +++++------------- 6 files changed, 196 insertions(+), 179 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index cd9d2e00e..1c809e3c9 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -51,6 +51,7 @@ library , containers >= 0.4 && < 0.9 , data-default >= 0.7 && < 0.9 , directory >= 1.3 && < 1.4 + , extra >= 1.7 && < 1.9 , fp-ieee >= 0.1 && < 0.2 , libBF >= 0.6.2 && < 0.7 , mtl >= 2.0 && < 2.4 @@ -60,7 +61,6 @@ library , process >= 1.6 && < 1.7 , random >= 1.1 && < 1.4 , transformers >= 0.5 && < 0.7 - , xml >= 1.3 && < 1.4 , what4 >= 1.3 && < 1.8 , copilot-core >= 4.7.1 && < 4.8 diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/AST.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/AST.hs index e69cda8d0..efd493a6d 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/AST.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/AST.hs @@ -1,27 +1,54 @@ {-# LANGUAGE Safe #-} --- | Abstract syntax tree of Kind2 files. +-- | Abstract syntax tree of Kind2 native input files. +-- +-- This represents the native transition system input format supported by +-- Kind2 1.0 and newer (@--input_format native@), in which a file is a +-- sequence of @define-node@ forms, each declaring a state transition system +-- by means of an initial state predicate and a transition relation predicate. +-- The last node defined in a file is the top system analyzed by Kind2, and +-- the properties to check are attached to it. +-- +-- The native input format is largely undocumented, and Kind2's user +-- documentation only covers its Lustre frontend. The best available +-- references for the native format are the parser in the Kind2 sources +-- ([nativeInput.ml] +-- (https://github.com/kind2-mc/kind2/blob/v3.0.0/src/nativeInput.ml)), +-- which includes a sketch of the grammar in a comment near the end of the +-- file) and the example file +-- [two_counters.kind2] +-- (https://github.com/kind2-mc/kind2/blob/develop/examples/two_counters.kind2). module Copilot.Theorem.Kind2.AST where --- | A file is a sequence of predicates and propositions. +-- | A file is a sequence of node definitions, together with a distinguished +-- top node and a series of propositions about the top node. +-- +-- Kind2 analyzes the last node defined in a file as the top system, so the +-- top node is always printed after all the other nodes. data File = File - { filePreds :: [PredDef] - , fileProps :: [Prop] } + { fileNodes :: [Node] -- ^ Nodes other than the top node, in dependency + -- order (a node may only refer to nodes defined + -- before it). + , fileTopNode :: Node -- ^ The top node, which the propositions are + -- attached to. + , fileProps :: [Prop] -- ^ Propositions about the top node. + } -- | A proposition is defined by a term. data Prop = Prop { propName :: String , propTerm :: Term } --- | A predicate definition. -data PredDef = PredDef - { predId :: String -- ^ Identifier for the predicate. - , predStateVars :: [StateVarDef] -- ^ Variables identifying the states in the - -- underlying state transition system. - , predInit :: Term -- ^ Predicate that holds for initial +-- | A node definition. +data Node = Node + { nodeId :: String -- ^ Identifier for the node. + , nodeStateVars :: [StateVarDef] -- ^ Variables identifying the states in + -- the underlying state transition system. + , nodeInit :: Term -- ^ Predicate that holds for initial -- states. - , predTrans :: Term -- ^ Predicate that holds for two states, if - -- there is state transition between them. + , nodeTrans :: Term -- ^ Predicate that holds for two states, + -- if there is a state transition between + -- them. } -- | A definition of a state variable. diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs index f80b8170b..8a1d29c28 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Output.hs @@ -4,66 +4,97 @@ -- | Parse output of Kind2. module Copilot.Theorem.Kind2.Output (parseOutput) where -import Text.XML.Light hiding (findChild) -import Copilot.Theorem.Prove as P -import Data.Maybe (fromJust) +import Data.List.Extra (isInfixOf, splitOn, stripInfix, trim) +import Data.Maybe (mapMaybe) + +import Copilot.Theorem.Prove as P import qualified Copilot.Core as C import qualified Copilot.Theorem.Misc.Error as Err -simpleName s = QName s Nothing Nothing - -- | Parse output of Kind2. +-- +-- The output is expected to be in XML format, as produced by Kind2's @-xml@ +-- flag and documented at +-- . +-- Note that this function does not parse the output as XML: when given a +-- system in the native transition system format, Kind2 produces output that +-- is not always well-formed XML (e.g., counterexamples to invariant +-- properties cannot be printed for systems in that format -- the +-- counterexample printing functions in Kind2's @src/inputSystem.ml@ fail with +-- an internal error for native input -- and Kind2 reports the error in the +-- middle of the XML output). Instead, this function looks for the +-- @\@ tag for the given property and extracts the answers in the +-- @\@ tags it contains. parseOutput :: String -- ^ Property whose validity is being checked. -> C.Prop -- ^ The property's quantifier. -> String -- ^ XML output of Kind2 -> P.Output -parseOutput propId propQuantifier xml = fromJust $ do - root <- parseXMLDoc xml - case findAnswer . findPropTag $ root of - "valid" -> case propQuantifier of - -- We encode a universally quantified property P as - -- ∀x.P(x) in Kind2, so the original property is valid - -- iff the Kind2 property is valid. - C.Forall {} -> return (Output Valid []) - -- We encode an existentially quantified property P as - -- ¬(∀x.¬(P(x))) in Kind2, so the original property is - -- invalid iff the Kind2 property is valid. - C.Exists {} -> return (Output Invalid []) - "falsifiable" -> case propQuantifier of - -- We encode a universally quantified property P as - -- ∀x.P(x) in Kind2, so the original property is invalid - -- iff the Kind2 property is invalid. - C.Forall {} -> return (Output Invalid []) - -- We encode an existentially quantified property P as - -- ¬(∀x.¬(P(x))) in Kind2, so the original property is - -- valid iff the Kind2 property is invalid. - C.Exists {} -> return (Output Valid []) - s -> err $ "Unrecognized status : " ++ s +parseOutput propId propQuantifier xml + | "valid" `elem` answers = quantified (Output Valid []) + (Output Invalid []) + | "falsifiable" `elem` answers = quantified (Output Invalid []) + (Output Valid []) + | "unknown" `elem` answers = Output Unknown [] + | null answers = err $ "Answer for property " ++ propId ++ " not found" + | otherwise = err $ "Unrecognized status : " ++ unwords answers where - searchForRuntimeError = undefined + -- Pick an output based on the quantifier of the property. The first + -- argument is returned when the property Kind2 was asked about is valid, + -- and the second one when it is invalid. + -- + -- We encode a universally quantified property P as ∀x.P(x) in Kind2, so + -- the original property is valid iff the Kind2 property is valid. + -- + -- We encode an existentially quantified property P as ¬(∀x.¬(P(x))) in + -- Kind2, so the original property is valid iff the Kind2 property is + -- invalid. + quantified ifValid ifInvalid = case propQuantifier of + C.Forall {} -> ifValid + C.Exists {} -> ifInvalid + + -- All the answers reported for the property, in the order in which they + -- appear in the output. A conclusive answer (valid or falsifiable), if + -- any, takes precedence over inconclusive (unknown) ones, which Kind2 may + -- also report when the analysis terminates without an answer for all + -- properties. + answers = mapMaybe answerText + $ filter isRightProperty + $ propertyElems xml - findPropTag root = - let rightElement elt = - qName (elName elt) == "Property" - && lookupAttr (simpleName "name") (elAttribs elt) - == Just propId - in case filterChildren rightElement root of - tag : _ -> tag - _ -> err $ "Tag for property " ++ propId ++ " not found" + -- Substrings of the output following a @\ - case onlyText (elContent answTag) of - answ : _ -> cdData answ - _ -> err "Invalid 'Answer' attribute" - _ -> err "Attribute 'Answer' not found" + -- True if the given property element has the name of the property this + -- function looks for. + isRightProperty elem' = + ("name=\"" ++ escapeAttr propId ++ "\"") `isInfixOf` openingTag + where + openingTag = takeWhile (/= '>') elem' + + -- The contents of the first @\@ tag in the given element, if + -- any. + answerText elem' = do + (_, rest) <- stripInfix "" rest + let answer = trim $ takeWhile (/= '<') rest' + if null answer then Nothing else Just answer err :: forall a . String -> a err msg = Err.fatal $ "Parse error while reading the Kind2 XML output : \n" ++ msg ++ "\n\n" ++ xml + +-- | Escape a string for use as an XML attribute value. +escapeAttr :: String -> String +escapeAttr = concatMap escapeChar + where + escapeChar '&' = "&" + escapeChar '<' = "<" + escapeChar '>' = ">" + escapeChar '"' = """ + escapeChar c = [c] diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/PrettyPrint.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/PrettyPrint.hs index 6851d46fa..92d158a1c 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/PrettyPrint.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/PrettyPrint.hs @@ -1,6 +1,8 @@ {-# LANGUAGE Safe #-} --- | Pretty print a Kind2 file defining predicates and propositions. +-- | Pretty print a Kind2 file defining nodes and propositions, in the native +-- transition system input format supported by Kind2 1.0 and newer (see +-- "Copilot.Theorem.Kind2.AST" for pointers to references on the format). module Copilot.Theorem.Kind2.PrettyPrint ( prettyPrint ) where import Copilot.Theorem.Misc.SExpr @@ -15,6 +17,14 @@ type SSExpr = SExpr String -- | Reserved keyword prime. kwPrime = "prime" +-- | Dummy position attached to the properties of the file. +-- +-- Kind2 requires a position (in the format @file:row-col@) for properties +-- declared with the @:user@ source annotation, and reports that position back +-- in its output. +propPosition :: String +propPosition = "copilot:1-1" + -- | Pretty print a Kind2 file. prettyPrint :: File -> String prettyPrint = @@ -29,30 +39,41 @@ shouldIndent (List [Atom a, Atom _]) = a `notElem` [kwPrime] shouldIndent _ = True -- | Convert a file into a sequence of expressions. +-- +-- The top node is printed last, since Kind2 analyzes the last node of the +-- file as the top system. The properties of the file are attached to it. ppFile :: File -> [SSExpr] -ppFile (File preds props) = map ppPredDef preds ++ ppProps props +ppFile (File nodes top props) = + map (`ppNode` []) nodes ++ [ppNode top (ppProps props)] --- | Convert a sequence of propositions into command to check each of them. +-- | Convert a sequence of propositions into a props field. ppProps :: [Prop] -> [SSExpr] -ppProps ps = [ node "check-prop" [ list $ map ppProp ps ] ] +ppProps [] = [] +ppProps ps = [ node "props" [ list $ map ppProp ps ] ] -- | Convert a proposition into an expression. ppProp :: Prop -> SSExpr -ppProp (Prop n t) = list [atom n, ppTerm t] +ppProp (Prop n t) = list [atom n, ppTerm t, atom ":user", atom propPosition] --- | Convert a predicate into an expression. -ppPredDef :: PredDef -> SSExpr -ppPredDef pd = - list [ atom "define-pred" - , atom (predId pd) - , list . map ppStateVarDef . predStateVars $ pd - , node "init" [ppTerm $ predInit pd] - , node "trans" [ppTerm $ predTrans pd] ] +-- | Convert a node, together with optional extra fields, into an expression. +ppNode :: Node -> [SSExpr] -> SSExpr +ppNode n extraFields = + list $ [ atom "define-node" + , atom (nodeId n) + , list . map ppStateVarDef . nodeStateVars $ n + , node "init" [ppTerm $ nodeInit n] + , node "trans" [ppTerm $ nodeTrans n] ] + ++ extraFields -- | Convert a state variable definition into an expression. ppStateVarDef :: StateVarDef -> SSExpr ppStateVarDef svd = - list [atom (varId svd), ppType (varType svd)] + list $ [atom (varId svd), ppType (varType svd)] + ++ map ppStateVarFlag (varFlags svd) + +-- | Convert a state variable option into an expression. +ppStateVarFlag :: StateVarFlag -> SSExpr +ppStateVarFlag FConst = atom ":const" -- | Convert a type into an expression. ppType :: Type -> SSExpr diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs index 4bb52322e..a79ede4f6 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Prover.hs @@ -28,7 +28,8 @@ import qualified Copilot.Theorem.TransSys as TS -- | Options for Kind2 data Options = Options { bmcMax :: Int -- ^ Upper bound on the number of unrolling that base and - -- step will perform. A value of 0 means /unlimited/. + -- step will perform (passed to Kind2 via its + -- @--unroll_max@ option). A value of 0 means /unlimited/. } -- | Default options with unlimited unrolling for base and step. @@ -41,7 +42,10 @@ data ProverST = ProverST -- | A prover backend based on Kind2. -- --- The executable @kind2@ must exist and its location be in the @PATH@. +-- The executable @kind2@ must exist and its location be in the @PATH@. Kind2 +-- version 1.0 or newer is required: the native input format and the +-- command-line options this prover uses were introduced in Kind2 1.0, and +-- remain supported as of Kind2 3.0. kind2Prover :: Options -> Prover kind2Prover opts = Prover { proverName = "Kind2" @@ -50,24 +54,22 @@ kind2Prover opts = Prover , closeProver = const $ return () } kind2Prog = "kind2" -kind2BaseOptions = ["--input-format", "native", "-xml"] +kind2BaseOptions = ["--input_format", "native", "-xml"] askKind2 :: ProverST -> [PropId] -> [PropId] -> IO Output askKind2 (ProverST opts spec) assumptions toCheck = do - let kind2Input = prettyPrint . toKind2 Inlined assumptions toCheck $ spec + let kind2Input = prettyPrint . toKind2 assumptions toCheck $ spec (tempName, tempHandle) <- openTempFile "." "out" "kind" hPutStr tempHandle kind2Input hClose tempHandle let kind2Options = - kind2BaseOptions ++ ["--bmc_max", show $ bmcMax opts, tempName] + kind2BaseOptions ++ ["--unroll_max", show $ bmcMax opts, tempName] (_, output, _) <- readProcessWithExitCode kind2Prog kind2Options "" - putStrLn kind2Input - removeFile tempName let propId = head toCheck diff --git a/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs b/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs index 123f6ed64..d59aaae24 100644 --- a/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/Kind2/Translate.hs @@ -1,68 +1,44 @@ -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE Safe #-} -{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE Safe #-} -- | Convert modular transition systems ('TransSys') into Kind2 file -- specifications. module Copilot.Theorem.Kind2.Translate ( toKind2 - , Style (..) ) where import Copilot.Theorem.TransSys import qualified Copilot.Theorem.Kind2.AST as K -import Control.Exception.Base (assert) - import Data.Function (on) import Data.Maybe (fromJust) -import Data.List (sort, sortBy) -import Data.Map (Map, (!)) +import Data.List (partition, sort, sortBy) +import Data.Map ((!)) import qualified Data.Map as Map import qualified Data.Bimap as Bimap --- The following properties MUST hold for the given transition system : --- * Nodes are sorted by topological order --- * Nodes are `completed`, which means the dependency graph is transitive --- and each node imports all the local variables of its dependencies --- - -type DepGraph = Map NodeId [NodeId] - --- | Style of the Kind2 files produced: modular (with multiple separate nodes), --- or all inlined (with only one node). --- --- In the modular style, the graph is simplified to remove cycles by collapsing --- all nodes participating in a strongly connected components. --- --- In the inlined style, the structure of the modular transition system is --- discarded and the graph is first turned into a /non-modular transition/ --- /system/ with only one node, which can be then converted into a Kind2 file. -data Style = Inlined | Modular - -- | Produce a Kind2 file that checks the properties specified. -toKind2 :: Style -- ^ Style of the file (modular or inlined). - -> [PropId] -- ^ Assumptions +toKind2 :: [PropId] -- ^ Assumptions -> [PropId] -- ^ Properties to be checked -> TransSys -- ^ Modular transition system holding the system spec -> K.File -toKind2 style assumptions checkedProps spec = - addAssumptions spec assumptions - $ trSpec (complete spec') predCallsGraph assumptions checkedProps +toKind2 assumptions checkedProps spec = + addAssumptions spec' assumptions $ trSpec spec' checkedProps where - predCallsGraph = specDependenciesGraph spec' - spec' = case style of - Inlined -> inline spec - Modular -> removeCycles spec + spec' = inline spec -trSpec :: TransSys -> DepGraph -> [PropId] -> [PropId] -> K.File -trSpec spec predCallsGraph _assumptions checkedProps = K.File preds props +trSpec :: TransSys -> [PropId] -> K.File +trSpec spec checkedProps = K.File otherNodes topNode props where - preds = map (trNode spec predCallsGraph) (specNodes spec) + (topNode, otherNodes) = + case partition ((== specTopNodeId spec) . K.nodeId) nodes of + ([top], others) -> (top, others) + _ -> error $ "Kind2.Translate: top node " + ++ specTopNodeId spec ++ " not found" + nodes = map (trNode spec) (specNodes spec) props = map trProp $ filter ((`elem` checkedProps) . fst) $ Map.toList $ Map.map fst $ specProps spec @@ -70,37 +46,30 @@ trSpec spec predCallsGraph _assumptions checkedProps = K.File preds props trProp :: (PropId, ExtVar) -> K.Prop trProp (pId, var) = K.Prop pId (trVar . extVarLocalPart $ var) -trNode :: TransSys -> DepGraph -> Node -> K.PredDef -trNode spec predCallsGraph node = - K.PredDef { K.predId, K.predStateVars, K.predInit, K.predTrans } - where - predId = nodeId node - predStateVars = gatherPredStateVars spec node - predInit = mkConj $ initLocals node - ++ map (trExpr False) (nodeConstrs node) - ++ predCalls True spec predCallsGraph node - predTrans = mkConj $ transLocals node - ++ map (trExpr True) (nodeConstrs node) - ++ predCalls False spec predCallsGraph node - +trNode :: TransSys -> Node -> K.Node +trNode spec node = K.Node + { K.nodeId = nodeId node + , K.nodeStateVars = gatherPredStateVars spec node + , K.nodeInit = mkConj $ initLocals node + ++ map (trExpr False) (nodeConstrs node) + , K.nodeTrans = mkConj $ transLocals node + ++ map (trExpr True) (nodeConstrs node) + } + +-- | Add the assumptions to the top node of the file by conjoining the +-- corresponding property variables to its initial state and transition +-- relation predicates. addAssumptions :: TransSys -> [PropId] -> K.File -> K.File -addAssumptions spec assumptions (K.File {K.filePreds, K.fileProps}) = - K.File (changeTail aux filePreds) fileProps +addAssumptions spec assumptions file = + file { K.fileTopNode = aux (K.fileTopNode file) } where - changeTail f (reverse -> l) = case l of - [] -> error "impossible" - x : xs -> reverse $ f x : xs - - aux pred = - let init' = mkConj ( K.predInit pred : map K.StateVar vars ) - trans' = mkConj ( K.predTrans pred : map K.PrimedStateVar vars ) - in pred { K.predInit = init', K.predTrans = trans' } + aux node = + let init' = mkConj ( K.nodeInit node : map K.StateVar vars ) + trans' = mkConj ( K.nodeTrans node : map K.PrimedStateVar vars ) + in node { K.nodeInit = init', K.nodeTrans = trans' } - vars = - let bindings = nodeImportedVars (specTopNode spec) - toExtVar a = fst $ fromJust $ Map.lookup a $ specProps spec - toTopVar (ExtVar nId v) = assert (nId == specTopNodeId spec) v - in map (varName . toTopVar . toExtVar) assumptions + toExtVar a = fst $ fromJust $ Map.lookup a $ specProps spec + vars = map (varName . extVarLocalPart . toExtVar) assumptions -- The ordering really matters here because the variables -- have to be given in this order in a pred call @@ -169,39 +138,6 @@ transLocals node = Expr e -> [mkEquality (trPrimedVar v) (trExpr True e)] Constrs cs -> map (trExpr True) cs -predCalls :: Bool -> TransSys -> DepGraph -> Node -> [K.Term] -predCalls isInitCall spec predCallsGraph node = - map mkCall toCall - where - nid = nodeId node - toCall = predCallsGraph ! nid - nodesMap = Map.fromList [(nodeId n, n) | n <- specNodes spec] - - nodeLocals n = - map (ExtVar n) . sort . Map.keys - . nodeLocalVars $ (nodesMap ! n) - - mkCall callee - | isInitCall = - K.PredApp callee K.Init (argsSeq trVar) - | otherwise = - K.PredApp callee K.Trans (argsSeq trVar ++ argsSeq trPrimedVar) - where - - calleeLocals = nodeLocals callee - calleeImported = - (concatMap nodeLocals . sort . nodeDependencies) $ nodesMap ! callee - - localAlias trVarF ev = - case Bimap.lookupR ev $ nodeImportedVars node of - Nothing -> error $ - "This spec is not complete : " - ++ show ev ++ " should be imported in " ++ nid - Just v -> trVarF v - - argsSeq trVarF = - map (localAlias trVarF) (calleeLocals ++ calleeImported) - trExpr :: Bool -> Expr t -> K.Term trExpr primed = tr where From 8258eb4968490d46bcb651c870b64d73398c7adb Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Mon, 22 Jun 2026 14:26:39 -0500 Subject: [PATCH 367/483] copilot-theorem: Update README for Kind2 backend changes. Refs #734. In order to keep Copilot effectively working in the current software ecosystem, we need to extend the versions of dependencies that Copilot can be installed with. kind2 has seen release 2.3.0, but copilot-theorem requires kind2 0.7.2. This commit updates copilot-theorem's README to reflect updates required to the Kind2 backend for supporting newer versions of Kind2 (e.g., the renaming Kind2's "--bmc_max" flag to "--unroll_max"). --- copilot-theorem/README.md | 40 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/copilot-theorem/README.md b/copilot-theorem/README.md index 64cce8995..47a4b6081 100644 --- a/copilot-theorem/README.md +++ b/copilot-theorem/README.md @@ -31,7 +31,8 @@ properties on Copilot programs. It includes: * The *copilot-core* and *copilot-prettyprinter* Haskell libraries * The *Yices2* SMT-solver: `yices-smt2` must be in your `$PATH` * The *Z3* SMT-solver: `z3` must be in your `$PATH` -* The *Kind2* model checker: `kind2` must be in your `$PATH` +* The *Kind2* model checker (version 1.0 or newer): `kind2` must be in your + `$PATH` To build it, just install the Copilot library as described in the top-level README. @@ -87,7 +88,9 @@ configuration. The *Kind2* prover uses the model checker with the same name, from the University of Iowa. It translates the Copilot specification into a *modular transition system* (the Kind2 native format) and then calls the `kind2` -executable. +executable. Kind2 version 1.0 or newer is required: the native input format +and the command-line options used by this prover were introduced in Kind2 +1.0, and remain supported as of Kind2 3.0. It is provided by the `Copilot.Theorem.Kind2` module, which exports a `kind2Prover :: Options -> Prover` where the `Options` type is defined as @@ -95,7 +98,7 @@ It is provided by the `Copilot.Theorem.Kind2` module, which exports a `kind2Prov ```haskell data Options = Options { bmcMax :: Int } ``` -and where `bmcMax` corresponds to the `--bmc_max` option of *kind2* and is +and where `bmcMax` corresponds to the `--unroll_max` option of *kind2* and is equivalent to the `maxK` option of the K-Induction prover. Its default value is 0, which stands for infinity. @@ -335,11 +338,16 @@ process. The transition system obtained by the `TransSys.Translate` module is perfectly consistent. However, it can't be directly translated into the *Kind2 native -file format*. Indeed, it is natural to bind each node to a predicate but the -Kind2 file format requires that each predicate only uses previously defined -predicates. However, some nodes in our transition system could be mutually -recursive. Therefore, the goal of the `removeCycles :: Spec -> Spec` function, -defined in `TransSys.Transform`, is to remove such dependency cycles. +file format*. The native input format of Kind2 (version 1.0 and newer) does +not support predicate calls inside the initial state and transition relation +predicates of a node, so the modular structure of the transition system cannot +be expressed directly in it. Therefore, the system is first turned into an +equivalent non-modular transition system with only one node by the +`inline :: Spec -> Spec` function, defined in `TransSys.Transform`: +```haskell +inline :: Spec -> Spec +inline spec = mergeNodes [nodeId n | n <- specNodes spec] spec +``` This function relies on the `mergeNodes :: [NodeId] -> Spec -> Spec` function, whose signature is self-explicit. The latter solves name conflicts by using the @@ -516,10 +524,14 @@ in the Kind2 SMT solver. #### Displaying counterexamples Counterexamples are not displayed with the Kind2 prover because Kind2 doesn't -support XML output of counterexamples. If the last feature is provided, it -should be easy to implement displaying of counterexamples in *copilot-theorem*. -For this, we recommend keeping some information about *observers* in -`TransSys.Spec` and to add one variable per observer in the Kind2 output file. +support the output of counterexamples for systems expressed in its native input +format (the counterexample printing functions in Kind2's +[`src/inputSystem.ml`](https://github.com/kind2-mc/kind2/blob/v3.0.0/src/inputSystem.ml) +fail with an internal error for native input). If Kind2 adds this feature, it +should be easy to implement displaying of counterexamples in +*copilot-theorem*. For this, we recommend keeping some information about +*observers* in `TransSys.Spec` and to add one variable per observer in the +Kind2 output file. #### Bad handling of non-linear operators and external functions @@ -569,14 +581,14 @@ architecture of Kind2. It is true that the code of `TransSys` is quite complex. In fact, it would be really straightforward to produce a flattened transition system and then a -Kind2 file with just a single *top* predicate. In fact, It would be as easy as +Kind2 file with just a single *top* node. In fact, It would be as easy as producing an *IL* specification. To be honest, I'm not sure producing a modular *Kind2* output is worth the complexity added. It's especially true at the time I write this in the sense that: -* Each predicate introduced is used only one time (which is true because +* Each node introduced is used only one time (which is true because Copilot doesn't handle functions or parameterized streams like Lustre does and everything is inlined during the reification process). * A similar form of structure could be obtained from a flattened Kind2 native From b8ff2fbaa16abac1441d172fa557e727d4380e66 Mon Sep 17 00:00:00 2001 From: Chris Hathhorn Date: Tue, 9 Jun 2026 13:54:17 -0500 Subject: [PATCH 368/483] copilot-theorem: Document changes in CHANGELOG. Refs #734. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 03facaf36..fd4dea25b 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-06-09 + * Extend range of versions of Kind2. (#734) + 2026-05-07 * Version bump (4.7.1). (#730) * Handle special Float values correctly for counterexamples. (#697) From 423e4bd95130c1148a0a8ab826d732892ee25468 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 369/483] copilot-core: Version bump (4.8). Refs #745. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index ee463977c..541cad1e3 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.7.1 +version: 4.8 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From 068fb4dd5f9f4be6457b722666674fd8d1ee0ad4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 370/483] copilot-bluespec: Version bump (4.8). Refs #745. --- copilot-bluespec/copilot-bluespec.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index ffaadaca2..2cff39b37 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.7.1 +version : 4.8 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,8 +51,8 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.7.1 && < 4.8 - , language-bluespec >= 0.1 && < 0.2 + , copilot-core >= 4.8 && < 4.9 + , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From e89270da21a828c45ddb39d73c02931f4973ffa2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 371/483] copilot-c99: Version bump (4.8). Refs #745. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index d9802bd42..d3defea3f 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.7.1 +version : 4.8 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.7.1 && < 4.8 + , copilot-core >= 4.8 && < 4.9 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From 2d8b28ad156c1546b39aa8cbef83468ba3c545b4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 372/483] copilot-prettyprinter: Version bump (4.8). Refs #745. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index c40511c43..393ae9b37 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.7.1 +version: 4.8 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.7.1 && < 4.8 + copilot-core >= 4.8 && < 4.9 exposed-modules: From 792d6d9bff09ecec03538e7d82e72779c7596397 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 373/483] copilot-interpreter: Version bump (4.8). Refs #745. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 0303bf17b..255d0400d 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.7.1 +version: 4.8 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.7.1 && < 4.8 + copilot-core >= 4.8 && < 4.9 exposed-modules: From bce90b0d81f566d61a0b4e136e4f2ee480217823 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 374/483] copilot-theorem: Version bump (4.8). Refs #745. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 1c809e3c9..28127ebd0 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.7.1 +version : 4.8 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , transformers >= 0.5 && < 0.7 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.7.1 && < 4.8 - , copilot-prettyprinter >= 4.7.1 && < 4.8 + , copilot-core >= 4.8 && < 4.9 + , copilot-prettyprinter >= 4.8 && < 4.9 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 40b284454e23c9c15963eeba2447e9e6f9c0ea55 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 375/483] copilot-language: Version bump (4.8). Refs #745. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index 2cfab8eb8..ca5232a8c 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.7.1 +version: 4.8 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.7.1 && < 4.8 - , copilot-interpreter >= 4.7.1 && < 4.8 - , copilot-theorem >= 4.7.1 && < 4.8 + , copilot-core >= 4.8 && < 4.9 + , copilot-interpreter >= 4.8 && < 4.9 + , copilot-theorem >= 4.8 && < 4.9 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From 27e4a4988ae552850ac4c9227a74fc682af14482 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 376/483] copilot-libraries: Version bump (4.8). Refs #745. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index 85b117f63..dd96c07d4 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.7.1 +version: 4.8 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.7.1 && < 4.8 + , copilot-language >= 4.8 && < 4.9 exposed-modules: Copilot.Library.Analysis From 766bf4f787fde14e88d6cf32e1e28c848b7b93c0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 377/483] copilot-verifier: Version bump (4.8). Refs #745. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 339892ec9..aed3a0f10 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.7.1 +Version: 4.8 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.7.1 && < 4.8, - copilot-core >= 4.7.1 && < 4.8, - copilot-theorem >= 4.7.1 && < 4.8, + copilot-c99 >= 4.8 && < 4.9, + copilot-core >= 4.8 && < 4.9, + copilot-theorem >= 4.8 && < 4.9, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.7.1 && < 4.8, - copilot-language >= 4.7.1 && < 4.8, - copilot-libraries >= 4.7.1 && < 4.8, - copilot-prettyprinter >= 4.7.1 && < 4.8, + copilot >= 4.8 && < 4.9, + copilot-language >= 4.8 && < 4.9, + copilot-libraries >= 4.8 && < 4.9, + copilot-prettyprinter >= 4.8 && < 4.9, copilot-verifier exposed-modules: Copilot.Verifier.Examples From cb850da8408028c9628697e87c7c81ef6a385e81 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 378/483] copilot-visualizer: Version bump (4.8). Refs #745. --- copilot-visualizer/copilot-visualizer.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 5e8735ea4..1bd88608a 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.7.1 +version: 4.8 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.7.1 && < 4.8, - copilot-core >= 4.7.1 && < 4.8, - copilot-interpreter >= 4.7.1 && < 4.8, - copilot-language >= 4.7.1 && < 4.8 + copilot >= 4.8 && < 4.9, + copilot-core >= 4.8 && < 4.9, + copilot-interpreter >= 4.8 && < 4.9, + copilot-language >= 4.8 && < 4.9 exposed-modules: From 2eabea860dbc45ec2b6f406d6484a15f854e6762 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:20:12 -0700 Subject: [PATCH 379/483] copilot: Version bump (4.8). Refs #745. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index ace61023d..85dd95ab0 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.7.1 +version: 4.8 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.7.1 && < 4.8 - , copilot-theorem >= 4.7.1 && < 4.8 - , copilot-language >= 4.7.1 && < 4.8 - , copilot-libraries >= 4.7.1 && < 4.8 - , copilot-c99 >= 4.7.1 && < 4.8 - , copilot-prettyprinter >= 4.7.1 && < 4.8 + , copilot-core >= 4.8 && < 4.9 + , copilot-theorem >= 4.8 && < 4.9 + , copilot-language >= 4.8 && < 4.9 + , copilot-libraries >= 4.8 && < 4.9 + , copilot-c99 >= 4.8 && < 4.9 + , copilot-prettyprinter >= 4.8 && < 4.9 exposed-modules: Language.Copilot, Language.Copilot.Main From 254abaa94f82bc8fd0b7b05d047c197f6c5c55d3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 380/483] copilot-core: Document changes in CHANGELOG. Refs #745. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 4a019a359..e37d60585 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From 88d7cf5abd9b1ff4171ca9635e5549bffddb2749 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 381/483] copilot-bluespec: Document changes in CHANGELOG. Refs #745. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 63ea6878b..1145b7a83 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) * Fix translation of special Float values to Bluespec. (#697) From ad9bce157314386f729048cdc9143414f40be55a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 382/483] copilot-c99: Document changes in CHANGELOG. Refs #745. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 74c6c1dd4..ba43b631d 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From 310c80ddf67e8dc10401da6a305189e54ba91575 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 383/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #745. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index a8e6d3e12..7ffd6c90d 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From e27694855d0c8a8ff6aa6ca1c106615da9266509 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 384/483] copilot-interpreter: Document changes in CHANGELOG. Refs #745. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 53984488e..d97f4575e 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From f45759c245e82df5095071acf926b41bf046f514 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 385/483] copilot-theorem: Document changes in CHANGELOG. Refs #745. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index fd4dea25b..ec8dafd5a 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2026-06-09 +2026-07-07 + * Version bump (4.8). (#745) * Extend range of versions of Kind2. (#734) 2026-05-07 From d67b8c87f752bc326d270801cad35c3afdde1ac7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 386/483] copilot-language: Document changes in CHANGELOG. Refs #745. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 069d20f5d..b162f316a 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From 2fddcea36646277ce88ce1f1719144156019b64e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 387/483] copilot-libraries: Document changes in CHANGELOG. Refs #745. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 19b510a63..b09286048 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) * Add stream analysis module. (#726) From d081479e1d86a9fc7479cb233d5c2b84b2c3ff17 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 388/483] copilot-verifier: Document changes in CHANGELOG. Refs #745. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index a2b4dd530..81d2534f7 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From 8d17366634029cb76a13d0a42f0622cf168a9192 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 389/483] copilot-visualizer: Document changes in CHANGELOG. Refs #745. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 0735d51ab..e4a74c508 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From 88a6761537c5d7ae0b0efcd48e6ce59bcae22788 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 7 Jul 2026 23:21:58 -0700 Subject: [PATCH 390/483] copilot: Document changes in CHANGELOG. Refs #745. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index f58dd30e6..f46d51f91 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-07-07 + * Version bump (4.8). (#745) + 2026-05-07 * Version bump (4.7.1). (#730) From ab2f6a50d4e9ef08e0f27d03275cacb329051f8f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 22:41:08 -0700 Subject: [PATCH 391/483] copilot: Disable secure mode in Cabal in Travis CI. Refs #753. CI is failing during the Haskell setup step with the following error: ``` /root.json does not have enough signatures signed with the appropriate keys ``` It is possible to circumvent this error by configuring Cabal to not operate in secure mode. This commit modifies the Travis CI script to disable secure mode when connecting to Hackage. To be able to modify the settings for that repository, we first have Cabal create a default configuration file. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c7c69118d..f157c7aa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,8 @@ before_install: - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH - cabal --version - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" + - cabal user-config init + - sed -i -e "s;-- \(secure.\) True;\1 False;g" ${HOME}/.cabal/config - travis_retry cabal update - git submodule update --remote From 9d6b38cf42822ae0301e193842eb7b6184280d6c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Tue, 8 Sep 2026 06:25:27 +0000 Subject: [PATCH 392/483] copilot: Document changes in CHANGELOG. Refs #753. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index f46d51f91..d497447c5 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Disable secure mode in Cabal in Travis CI. (#753) + 2026-07-07 * Version bump (4.8). (#745) From 68c299123faeb92ece2223b90394da7ab7d03945 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 23 Aug 2026 19:02:15 +0000 Subject: [PATCH 393/483] copilot-visualizer: Fix inconsistent version bounds for `copilot` in Cabal file. Refs #749. The cabal file of `copilot-visualizer` lists inconsistent version bounds for the package `copilot`. Specifically, it depends on `copilot` versions from 4.8 to, but not including, 4.9 for the visualizer library, and from 4.7 up to, but not including, 4.8 for the `gui-example` executable. This prevents the example from being compiled against the same version of the library. This commit fixes the version bounds on `copilot` in the `gui-example` executable so that they match the version bounds applied to the `copilot-visualizer` library. --- copilot-visualizer/copilot-visualizer.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 1bd88608a..4284c9886 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.7 && < 4.8, + copilot >= 4.8 && < 4.9, copilot-visualizer if flag(examples) From b58fab18c8065aa1311e053809d9f7befe78ab2b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 23 Aug 2026 19:05:39 +0000 Subject: [PATCH 394/483] copilot-visualizer: Document changes in CHANGELOG. Refs #749. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index e4a74c508..5e16e81f4 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-08-23 + * Fix inconsistent version bounds for `copilot` in Cabal file. (#749) + 2026-07-07 * Version bump (4.8). (#745) From c191a7365122492eef4cfbc7ae722d76eea97cc4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:33 -0700 Subject: [PATCH 395/483] copilot-bluespec: Replace tab in CHANGELOG. Refs #755. The CHANGELOG of the `copilot-bluespec` package contains a tab character. Per our style guide, we should not use tabs anywhere in code, READMEs or CHANGELOGs, unless strictly necessary. This commit replaces a tab in the CHANGELOG of `copilot-bluespec` with spaces. --- copilot-bluespec/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 1145b7a83..f47fdd362 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -11,7 +11,7 @@ 2026-01-07 * Version bump (4.6.1). (#705) * Disable test suite via newly added manual cabal flag. (#673) - * Fix translation of constant floating-point values. (#696) + * Fix translation of constant floating-point values. (#696) 2025-11-07 * Version bump (4.6). (#679) From 181d8033634c91cb747ef3b59c57dded2d3fbc04 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 7 May 2026 23:59:33 -0700 Subject: [PATCH 396/483] copilot-bluespec: Document changes in CHANGELOG. Refs #755. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index f47fdd362..29e143842 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2026-08-22 + * Replace tab in CHANGELOG. (#755) + 2026-07-07 * Version bump (4.8). (#745) From ddeb5985817a0be1a75df8e9c7aa1215e4b19db1 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 30 Jul 2026 23:08:01 -0700 Subject: [PATCH 397/483] copilot-visualizer: Relax version constraints on `ogma-extra`. Refs #757. In order to keep Copilot effectively working in the current Haskell ecosystem, as well as new versions of GHC, we need to extend the versions of dependencies that Copilot can be installed with. The package `ogma-extra` has seen release 1.15 and will soon see release 1.16, but `copilot-visualizer` depend on versions prior to 1.13. This commit relaxes the version constrains on `ogma-extra` for the `copilot-visualizer` package. --- copilot-visualizer/copilot-visualizer.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 4284c9886..bb523bfc3 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -61,7 +61,7 @@ library filepath >= 1.4.2 && < 1.6, hint >= 0.9.0 && < 1.10, pretty >= 1.0 && < 1.2, - ogma-extra >= 1.6.0 && < 1.13, + ogma-extra >= 1.6.0 && < 1.17, text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, From 0743773609d47c3a0070e627239c5596ccd2f2d8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 30 Jul 2026 23:10:12 -0700 Subject: [PATCH 398/483] copilot-visualizer: Document changes in CHANGELOG. Refs #757. --- copilot-visualizer/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 5e16e81f4..5c358e534 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,5 +1,6 @@ 2026-08-23 * Fix inconsistent version bounds for `copilot` in Cabal file. (#749) + * Relax version constraints on `ogma-extra`. (#757) 2026-07-07 * Version bump (4.8). (#745) From 0f1ebfc759daaaf27a01dc3f5b4fc25cda8ddca9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 21 Aug 2026 19:20:54 -0700 Subject: [PATCH 399/483] copilot-theorem: Remove unnecessary extension. Refs #733. The module `copilot-theorem:Copilot.Theorem.What4.Translate` enables the language extension `PatternSynonyms` unnecessarily. Nothing in the module requires that extension and it should continue to compile without it. Per our style guide to remove unused code and minimize extensions, this extension should be removed from that module. This commit removes the unnecessary extension from the `copilot-theorem:Copilot.Theorem.What4.Translate` module. --- copilot-theorem/src/Copilot/Theorem/What4/Translate.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs index cef4829ee..00edf8744 100644 --- a/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs +++ b/copilot-theorem/src/Copilot/Theorem/What4/Translate.hs @@ -2,7 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} From a26633e64929c8f19d0cb922e9acb39a07460c61 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 21 Aug 2026 20:12:01 -0700 Subject: [PATCH 400/483] copilot-theorem: Document changes in CHANGELOG. Refs #733. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index ec8dafd5a..140644e06 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-08-21 + * Remove unnecessary extension. (#733) + 2026-07-07 * Version bump (4.8). (#745) * Extend range of versions of Kind2. (#734) From 2489e6a227824dadd57bdccd9899600a1a02bf26 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 401/483] copilot-core: Version bump (4.8.1). Refs #760. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 541cad1e3..57f92d703 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.8 +version: 4.8.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From 1456edb773725a174066f731f86191e672fb4b14 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 402/483] copilot-bluespec: Version bump (4.8.1). Refs #760. --- copilot-bluespec/copilot-bluespec.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index 2cff39b37..e8f29e4d7 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.8 +version : 4.8.1 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,7 +51,7 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.8 && < 4.9 + , copilot-core >= 4.8.1 && < 4.9 , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From 2cc9ce1a0750c42e27636f032272231c32b8f99c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 403/483] copilot-c99: Version bump (4.8.1). Refs #760. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index d3defea3f..ea2e233dc 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.8 +version : 4.8.1 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.8 && < 4.9 + , copilot-core >= 4.8.1 && < 4.9 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From 80b8ed216fd791cf4c23efd1f14affbdea5b511e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 404/483] copilot-prettyprinter: Version bump (4.8.1). Refs #760. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index 393ae9b37..d6c2bcc11 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.8 +version: 4.8.1 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.8 && < 4.9 + copilot-core >= 4.8.1 && < 4.9 exposed-modules: From 6c233a078660e35ce9d9acc0b485b0f793430d11 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 405/483] copilot-interpreter: Version bump (4.8.1). Refs #760. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 255d0400d..538237a10 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.8 +version: 4.8.1 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.8 && < 4.9 + copilot-core >= 4.8.1 && < 4.9 exposed-modules: From ac3e269d6669d890adf5e9a456c691d7bb3451fd Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 406/483] copilot-theorem: Version bump (4.8.1). Refs #760. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index 28127ebd0..cf3e8c44c 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.8 +version : 4.8.1 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , transformers >= 0.5 && < 0.7 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.8 && < 4.9 - , copilot-prettyprinter >= 4.8 && < 4.9 + , copilot-core >= 4.8.1 && < 4.9 + , copilot-prettyprinter >= 4.8.1 && < 4.9 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 54f48abbf4de3d99c7bf7c761a56294ecde8a6ce Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 407/483] copilot-language: Version bump (4.8.1). Refs #760. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index ca5232a8c..b30c29db6 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.8 +version: 4.8.1 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.8 && < 4.9 - , copilot-interpreter >= 4.8 && < 4.9 - , copilot-theorem >= 4.8 && < 4.9 + , copilot-core >= 4.8.1 && < 4.9 + , copilot-interpreter >= 4.8.1 && < 4.9 + , copilot-theorem >= 4.8.1 && < 4.9 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From 163025363de77403c37c86cbf8663d6bae22766b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 408/483] copilot-libraries: Version bump (4.8.1). Refs #760. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index dd96c07d4..aa5c8e61b 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.8 +version: 4.8.1 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.8 && < 4.9 + , copilot-language >= 4.8.1 && < 4.9 exposed-modules: Copilot.Library.Analysis From 512df9749adaefa13cbdf61c0974775458a6d6de Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 409/483] copilot-verifier: Version bump (4.8.1). Refs #760. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index aed3a0f10..4052ee006 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.8 +Version: 4.8.1 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.8 && < 4.9, - copilot-core >= 4.8 && < 4.9, - copilot-theorem >= 4.8 && < 4.9, + copilot-c99 >= 4.8.1 && < 4.9, + copilot-core >= 4.8.1 && < 4.9, + copilot-theorem >= 4.8.1 && < 4.9, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.8 && < 4.9, - copilot-language >= 4.8 && < 4.9, - copilot-libraries >= 4.8 && < 4.9, - copilot-prettyprinter >= 4.8 && < 4.9, + copilot >= 4.8.1 && < 4.9, + copilot-language >= 4.8.1 && < 4.9, + copilot-libraries >= 4.8.1 && < 4.9, + copilot-prettyprinter >= 4.8.1 && < 4.9, copilot-verifier exposed-modules: Copilot.Verifier.Examples From c9b995ff5de9a0c717ee683a7016d81db47cfe5f Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 410/483] copilot-visualizer: Version bump (4.8.1). Refs #760. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index bb523bfc3..3b1d539eb 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.8 +version: 4.8.1 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.8 && < 4.9, - copilot-core >= 4.8 && < 4.9, - copilot-interpreter >= 4.8 && < 4.9, - copilot-language >= 4.8 && < 4.9 + copilot >= 4.8.1 && < 4.9, + copilot-core >= 4.8.1 && < 4.9, + copilot-interpreter >= 4.8.1 && < 4.9, + copilot-language >= 4.8.1 && < 4.9 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.8 && < 4.9, + copilot >= 4.8.1 && < 4.9, copilot-visualizer if flag(examples) From 0719333a6df5eb0414876cd9e5eb0671f87f6db9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:06:17 -0700 Subject: [PATCH 411/483] copilot: Version bump (4.8.1). Refs #760. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 85dd95ab0..5fa90035e 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.8 +version: 4.8.1 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.8 && < 4.9 - , copilot-theorem >= 4.8 && < 4.9 - , copilot-language >= 4.8 && < 4.9 - , copilot-libraries >= 4.8 && < 4.9 - , copilot-c99 >= 4.8 && < 4.9 - , copilot-prettyprinter >= 4.8 && < 4.9 + , copilot-core >= 4.8.1 && < 4.9 + , copilot-theorem >= 4.8.1 && < 4.9 + , copilot-language >= 4.8.1 && < 4.9 + , copilot-libraries >= 4.8.1 && < 4.9 + , copilot-c99 >= 4.8.1 && < 4.9 + , copilot-prettyprinter >= 4.8.1 && < 4.9 exposed-modules: Language.Copilot, Language.Copilot.Main From 69468e7f7b3b05a18faafe8822befa442d92f755 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 412/483] copilot-core: Document changes in CHANGELOG. Refs #760. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index e37d60585..1ccbf7db4 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From a0c12ad617d16b3f6f532b9785da90ffd6d01134 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 413/483] copilot-bluespec: Document changes in CHANGELOG. Refs #760. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 29e143842..3b655c5ac 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2026-08-22 +2026-09-07 + * Version bump (4.8.1). (#760) * Replace tab in CHANGELOG. (#755) 2026-07-07 From 1e8c8be5c72c5fd3f69e51a21941309fc0e4f9bd Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 414/483] copilot-c99: Document changes in CHANGELOG. Refs #760. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index ba43b631d..2caf70ef2 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 8f84c7646e09f5defee92f52b968db8cfe4c25fc Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 415/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #760. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 7ffd6c90d..799783ce8 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 6ad1e513408324257feb1a2da43e87db074dccaf Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 416/483] copilot-interpreter: Document changes in CHANGELOG. Refs #760. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index d97f4575e..3e396d8d4 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 5659bf71213834c601e771c739164c8b2c90f307 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 417/483] copilot-theorem: Document changes in CHANGELOG. Refs #760. --- copilot-theorem/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 140644e06..251b49484 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,4 +1,5 @@ -2026-08-21 +2026-09-07 + * Version bump (4.8.1). (#760) * Remove unnecessary extension. (#733) 2026-07-07 From 3f21a9b5ef73203a3f53b7f943e0195f5792dfc8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 418/483] copilot-language: Document changes in CHANGELOG. Refs #760. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index b162f316a..49162ceb0 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 2dee7e0dd00a063cce8a6338e9f696581131da3e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 419/483] copilot-libraries: Document changes in CHANGELOG. Refs #760. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index b09286048..0e8dcc83f 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 0206d92bc80463455a973ee57654ecf8dad1d2a0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 420/483] copilot-verifier: Document changes in CHANGELOG. Refs #760. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 81d2534f7..1dec1cfd0 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-09-07 + * Version bump (4.8.1). (#760) + 2026-07-07 * Version bump (4.8). (#745) From 5ce32a8d4c0b9a968c284c325e16b30872cc2985 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 421/483] copilot-visualizer: Document changes in CHANGELOG. Refs #760. --- copilot-visualizer/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 5c358e534..9d772eca0 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,4 +1,5 @@ -2026-08-23 +2026-09-07 + * Version bump (4.8.1). (#760) * Fix inconsistent version bounds for `copilot` in Cabal file. (#749) * Relax version constraints on `ogma-extra`. (#757) From 1f26417f9a83d12f1d0236ffa4dce5c7907506eb Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 7 Sep 2026 10:09:38 -0700 Subject: [PATCH 422/483] copilot: Document changes in CHANGELOG. Refs #760. --- copilot/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index d497447c5..a838b070a 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ 2026-09-07 + * Version bump (4.8.1). (#760) * Disable secure mode in Cabal in Travis CI. (#753) 2026-07-07 From 06c31d052cdbc4be7507b9b041b48ecdbb6354b4 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 11 Oct 2026 08:38:45 -0700 Subject: [PATCH 423/483] copilot-core: Force evaluation of `Proxy` value in `Copilot.Core.Type.Array`. Refs #767. The module `copilot-core:Copilot.Core.Type.Array` achieves less than full coverage in the two of the three main categories used by `hpc`, namely "Top Level Definitions" and "Expressions". For the former, only 4 out of 6 definitions are covered, and for the latter, only 47 out of 48. To make Copilot ready for use in future missions, we want to achieve maximum coverage possible. The lower coverage of "Expressions" is due to a `Proxy` value not being forced in a call to `natVal`, which only needs the type-level information for the `Proxy` being provided. This commit modifies the code of `copilot-core:Copilot.Core.Type.Array` to force the evaluation of a `Proxy` value being passed to `natVal`. Specifically, it uses `($!)`, which achieves the evaluation of the value at the point where it is being used, making `hpc` mark it as evaluated. Other alternatives (e.g., use of `seq`, use of `Proxy#`) either do not achieve the desired effect or require more extensive changes and the use of language extensions. --- copilot-core/src/Copilot/Core/Type/Array.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/src/Copilot/Core/Type/Array.hs b/copilot-core/src/Copilot/Core/Type/Array.hs index b11e8949b..07c10ba31 100644 --- a/copilot-core/src/Copilot/Core/Type/Array.hs +++ b/copilot-core/src/Copilot/Core/Type/Array.hs @@ -37,7 +37,7 @@ array xs | datalen == typelen = Array xs | otherwise = error errmsg where datalen = length xs - typelen = fromIntegral $ natVal (Proxy :: Proxy n) + typelen = fromIntegral $ natVal $! (Proxy :: Proxy n) errmsg = "Length of data (" ++ show datalen ++ ") does not match length of type (" ++ show typelen ++ ")." From fa1d30a20a222d551c37a22be7203f71c2a1263e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 11 Oct 2026 08:39:08 -0700 Subject: [PATCH 424/483] copilot-core: Test default functions in `Show` instance for `Array`. Refs #767. The module `copilot-core:Copilot.Core.Type.Array` achieves less than full coverage in the two of the three main categories used by `hpc`, namely "Top Level Definitions" and "Expressions". For the former, only 4 out of 6 definitions are covered, and for the latter, only 47 out of 48. To make Copilot ready for use in future missions, we want to achieve maximum coverage possible. The module `copilot-core:Copilot.Core.Type.Array` includes an instance of the class `Show` for the type `Array`. That instance implicitly declares `showList` and `showsPrec`, set to their default values per the class definition. However, there is no guarantee that the definitions are correct for this specific instance, and `hpc` marks them as not covered unless we explicitly test them, affecting the "Top Level Definitions" score for this library. This commit adds two tests for functions that are implicitly set to their default definitions in the `Show` instance for `Array`. --- .../tests/Test/Copilot/Core/Type/Array.hs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type/Array.hs b/copilot-core/tests/Test/Copilot/Core/Type/Array.hs index e070f138b..0af0ffae9 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type/Array.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type/Array.hs @@ -11,8 +11,9 @@ import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.QuickCheck (Gen, Property, arbitrary, chooseInt, expectFailure, forAll, - getNegative, getNonNegative, oneof, - property, vector, vectorOf) + getNegative, getNonNegative, + listOf, oneof, property, vector, + vectorOf) -- Internal imports: library modules being tested import Copilot.Core.Type.Array (Array, array, arrayElems, arrayUpdate) @@ -31,6 +32,10 @@ tests = testArrayElemsFail , testProperty "Show for arrays" testShowArray + , testProperty "showsPrec for arrays" + testShowsPrecArray + , testProperty "showList for arrays" + testShowListArray , testProperty "arrayElems (arrayUpdate x i v) !! i == v" (testArrayUpdateElem (Proxy :: Proxy 5)) , testProperty "arrayUpdate x i ((arrayElems x) !! i) == x" @@ -70,6 +75,17 @@ testShowArray :: Property testShowArray = forAll (vector 3) $ \l -> property $ show (array l :: Array 3 Int64) == show (l :: [Int64]) +-- | Test 'Show.showsPrec' for 'Array's. +testShowsPrecArray :: Property +testShowsPrecArray = forAll (getNonNegative <$> arbitrary) $ \prec -> + forAll (vector 3) $ \l -> property $ + showsPrec prec (array l :: Array 3 Int64) "" == show (l :: [Int64]) + +-- | Test 'Show.showList' for 'Array's. +testShowListArray :: Property +testShowListArray = forAll (listOf $ vector 3) $ \l -> property $ + showList (map array l :: [Array 3 Int64]) "" == show (l :: [[Int64]]) + -- | Test that updating an array updates the element appropriately (if we -- project that element we get the value we put in). testArrayUpdateElem :: forall n . KnownNat n => Proxy n -> Property From 34cc90741e8b792d8f55ad7e875a3bdcffe4a122 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 11 Oct 2026 17:16:56 -0700 Subject: [PATCH 425/483] copilot-core: Document changes in CHANGELOG. Refs #767. --- copilot-core/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 1ccbf7db4..33cf7c007 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,3 +1,6 @@ +2026-10-11 + * Increase test coverage for `Copilot.Core.Type.Array`. (#767) + 2026-09-07 * Version bump (4.8.1). (#760) From ae91b9ad23ddf0f9313ae9f6e3f5d9352969dfb6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 09:08:19 -0700 Subject: [PATCH 426/483] copilot-core: Fix errors in the documentation of `Copilot.Core.Spec`. Refs #769. The Haddock documentation associated to the type `copilot-core:Copilot.Core.Spec.Stream` contains two grammatical errors. The first is the incorrect use of "in" in the sentence "A stream in an infinite succession of values", which causes the sentence to have no verb, and the second is the use of `Stream` in the second paragraph with no article. To make documentation most useful and in compliance with our style guide, the documentation should have no typos or grammatical errors. This commit fixes both grammatical errors in the documentation of `copilot-core:Copilot.Core.Spec.Stream`, as well as a grammatical error in the top-level documentation of the same module. --- copilot-core/src/Copilot/Core/Spec.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/copilot-core/src/Copilot/Core/Spec.hs b/copilot-core/src/Copilot/Core/Spec.hs index 6614d0621..e8972c928 100644 --- a/copilot-core/src/Copilot/Core/Spec.hs +++ b/copilot-core/src/Copilot/Core/Spec.hs @@ -11,9 +11,9 @@ -- and their handling functions, or streams to observe, or a theorem that must -- be proved. -- --- In order to be executed, high-level Copilot Language Spec must be turned --- into Copilot Core's 'Spec'. This module defines the low-level Copilot Core --- representations for Specs and the main types of element in a spec. +-- In order to be executed, a high-level Copilot Language @Spec@ must be +-- turned into a Copilot Core 'Spec'. This module defines the low-level Copilot +-- Core representation for 'Spec' and the main types of element in a spec. module Copilot.Core.Spec ( Stream (..) , Observer (..) @@ -32,9 +32,9 @@ import Data.Typeable (Typeable) import Copilot.Core.Expr (Expr, Id, Name, UExpr) import Copilot.Core.Type (Type, Typed) --- | A stream in an infinite succession of values of the same type. +-- | A stream is an infinite succession of values of the same type. -- --- Stream can carry different types of data. Boolean streams play a special +-- Streams can carry different types of data. Boolean streams play a special -- role: they are used by other parts (e.g., 'Trigger') to detect when the -- properties being monitored are violated. data Stream = forall a . (Typeable a, Typed a) => Stream From ae2dfdb87f54b2288b5e95147830a203bf344382 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 09:09:50 -0700 Subject: [PATCH 427/483] copilot-core: Document changes in CHANGELOG. Refs #769. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 33cf7c007..9e45e6c5b 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,5 +1,6 @@ -2026-10-11 +2026-10-12 * Increase test coverage for `Copilot.Core.Type.Array`. (#767) + * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) 2026-09-07 * Version bump (4.8.1). (#760) From d567a73e4f00f126b9ed49b7b6065240fa120014 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:12:14 -0700 Subject: [PATCH 428/483] copilot-core: Replace tab in CHANGELOG. Refs #772. Several files across the codebase contain tabs, including the `DESIGN.md` file in `copilot-bluespec`, the `CHANGELOG` for `copilot-core`, and the `Eval` module in `copilot-interpreter`. Per our style guide, we should not use tabs anywhere in code, READMEs or CHANGELOGs, unless strictly necessary. This commit replaces a tab in the CHANGELOG of `copilot-core` with spaces. --- copilot-core/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 9e45e6c5b..755a52efa 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,5 +1,5 @@ 2026-10-12 - * Increase test coverage for `Copilot.Core.Type.Array`. (#767) + * Increase test coverage for `Copilot.Core.Type.Array`. (#767) * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) 2026-09-07 From 8f59941cd05980f4c9f4cf8ae77e821e5e1cb03e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:13:31 -0700 Subject: [PATCH 429/483] copilot-bluespec: Replace tabs in `DESIGN.md`. Refs #772. Several files across the codebase contain tabs, including the `DESIGN.md` file in `copilot-bluespec`, the `CHANGELOG` for `copilot-core`, and the `Eval` module in `copilot-interpreter`. Per our style guide, we should not use tabs anywhere in code, READMEs or CHANGELOGs, unless strictly necessary. This commit replaces tabs in the `DESIGN.md` document of `copilot-bluespec` with spaces. --- copilot-bluespec/DESIGN.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/copilot-bluespec/DESIGN.md b/copilot-bluespec/DESIGN.md index b5a07dd31..6cf5cd610 100644 --- a/copilot-bluespec/DESIGN.md +++ b/copilot-bluespec/DESIGN.md @@ -418,15 +418,15 @@ This generates a Verilog module `mkFibs.v` that looks like the following: ```verilog module mkFibs(CLK, - RST_N, + RST_N, - even_guard, + even_guard, - even_arg0, + even_arg0, - odd_guard, + odd_guard, - odd_arg0); + odd_arg0); input CLK; input RST_N; @@ -815,17 +815,17 @@ that looks like the following: ```verilog module mkFibs(CLK, - RST_N, + RST_N, - even_guard, + even_guard, - even_arg0, + even_arg0, - odd_guard, + odd_guard, - odd_arg0, + odd_arg0, - fibs); + fibs); input CLK; input RST_N; From de586c06d696053c7d7e5fdd70d99a220aac32c8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:12:42 -0700 Subject: [PATCH 430/483] copilot-interpreter: Replace tab in `Copilot.Interpret.Eval`. Refs #772. Several files across the codebase contain tabs, including the `DESIGN.md` file in `copilot-bluespec`, the `CHANGELOG` for `copilot-core`, and the `Eval` module in `copilot-interpreter`. Per our style guide, we should not use tabs anywhere in code, READMEs or CHANGELOGs, unless strictly necessary. This commit replaces a tab in the module `copilot-interpreter:Copilot.Interpret.Eval` with spaces. --- copilot-interpreter/src/Copilot/Interpret/Eval.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-interpreter/src/Copilot/Interpret/Eval.hs b/copilot-interpreter/src/Copilot/Interpret/Eval.hs index 462b5853e..9543806c9 100644 --- a/copilot-interpreter/src/Copilot/Interpret/Eval.hs +++ b/copilot-interpreter/src/Copilot/Interpret/Eval.hs @@ -19,7 +19,7 @@ module Copilot.Interpret.Eval import Copilot.Core (Expr (..), Field (..), Id, Name, Observer (..), Op1 (..), Op2 (..), Op3 (..), Spec, Stream (..), Trigger (..), Type (..), UExpr (..), Value (..), - arrayElems, arrayUpdate, specObservers, + arrayElems, arrayUpdate, specObservers, specStreams, specTriggers, updateField) import Copilot.Interpret.Error (badUsage) From 9835d834d41bdcea5abc4f83c214af88635c8e0a Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:33:19 -0700 Subject: [PATCH 431/483] copilot-core: Document changes in CHANGELOG. Refs #772. --- copilot-core/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 755a52efa..8ffeafc40 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,6 +1,7 @@ 2026-10-12 * Increase test coverage for `Copilot.Core.Type.Array`. (#767) * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) + * Replace tab in CHANGELOG. (#772) 2026-09-07 * Version bump (4.8.1). (#760) From 1e34f7b0f1e7aadff87c5f77f8ef5042c3e70930 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:33:30 -0700 Subject: [PATCH 432/483] copilot-bluespec: Document changes in CHANGELOG. Refs #772. --- copilot-bluespec/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index 3b655c5ac..aa122183f 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,3 +1,6 @@ +2026-10-12 + * Replace tabs in `DESIGN.md`. (#772) + 2026-09-07 * Version bump (4.8.1). (#760) * Replace tab in CHANGELOG. (#755) From d00808a471e635c470444edcaaa89478ee9fb0c9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:33:39 -0700 Subject: [PATCH 433/483] copilot-interpreter: Document changes in CHANGELOG. Refs #772. --- copilot-interpreter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 3e396d8d4..1b88d8eb7 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,3 +1,6 @@ +2026-10-12 + * Replace tab in `Copilot.Interpreter.Eval`. (#772) + 2026-09-07 * Version bump (4.8.1). (#760) From cfb8974ddb668f047b026241faa50011ed2c5411 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:18:43 -0700 Subject: [PATCH 434/483] copilot: Adjust Travis CI script to flag presence of tabs as an error. Refs #774. The current CI settings do not detect if tabs have been introduced in source code, documentation or CHANGELOGs. That lack of automation makes the accidental introduction of tabs in the code more likely, especially by new users who have not yet configured their editor to replace tabs with spaces. To facilitate keeping Copilot conformant with the style guide, we want to automatically detect when tabs are introduced and warn users accordingly. This commit modifies the Travis CI script to stop with an error if any code, Cabal files, documentation or CHANGELOGs contain any tabs. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index f157c7aa8..de3f14bb5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,9 @@ before_install: - git submodule update --remote script: + # Fail if the code, Cabal files, documentation or CHANGELOGs contain tabs. + - if grep -rP '\t' --include='*.hs' --include='*.cabal' --include='*.md' --include='CHANGELOG' . ; then exit 1; fi + # We explicitly install all libraries so that they are exposed and we can use # them for tests (e.g., with runhaskell). There is no harm in doing this # instead of installing just copilot. From 74a4e39b4e258767a9cea632b0b72e14c44319ee Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 10:34:18 -0700 Subject: [PATCH 435/483] copilot: Document changes in CHANGELOG. Refs #774. --- copilot/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index a838b070a..e7b1e3700 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,3 +1,6 @@ +2026-10-12 + * Adjust Travis CI script to flag presence of tabs as an error. (#774) + 2026-09-07 * Version bump (4.8.1). (#760) * Disable secure mode in Cabal in Travis CI. (#753) From 73c31d9de618cb99dd0dfafb47b9db34fec02fa7 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 13:54:30 -0700 Subject: [PATCH 436/483] copilot-bluespec: Remove trailing spaces. Refs #775. The module `copilot-bluespec:Copilot.Compile.Bluespec.Expr` contains trailing spaces. It's best to remove them, since those are automatically removed by many editors, leading to PRs containing changes unrelated to the main issues being addressed. In compliance with our coding standard, trailing space should be removed. This commit removes all trailing spaces from `copilot-bluespec:Copilot.Compile.Bluespec.Expr`. --- .../src/Copilot/Compile/Bluespec/Expr.hs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs index 2b71b3957..84a9ffe7a 100644 --- a/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs +++ b/copilot-bluespec/src/Copilot/Compile/Bluespec/Expr.hs @@ -627,43 +627,43 @@ constInt ty i -- | Translate a Copilot floating-point literal into a Bluespec expression. constFP :: Type ty -> Double -> BS.CExpr constFP ty d - -- Bluespec internally represents all IEEE floating-point values as structs + -- Bluespec internally represents all IEEE floating-point values as structs -- so special symbols have to be called for constructing special values, -- e.g. NaN, inf, and -0.0. See -- https://github.com/B-Lang-org/bsc/blob/main/src/Libraries/Base3-Math/FloatingPoint.bsv#L441 - | isInfinite d = BS.CApply - (BS.CVar $ BS.mkId BS.NoPos "infinity") + | isInfinite d = BS.CApply + (BS.CVar $ BS.mkId BS.NoPos "infinity") [ if d < 0 - then BS.CCon BS.idTrue [] + then BS.CCon BS.idTrue [] else BS.CCon BS.idFalse [] ] - - | isNaN d = let - cexpr = BS.CHasType + + | isNaN d = let + cexpr = BS.CHasType ( BS.CLit $ BS.CLiteral BS.NoPos $ BS.LInt $ BS.IntLit Nothing 10 cval ) - ( BS.CQType [] $ BS.TCon $ - BS.TyCon - (BS.mkId BS.NoPos ctype) - Nothing + ( BS.CQType [] $ BS.TCon $ + BS.TyCon + (BS.mkId BS.NoPos ctype) + Nothing BS.TIabstract ) - cval = case ty of - Float -> fromIntegral $ + cval = case ty of + Float -> fromIntegral $ castFloatToWord32 $ uncastDoubleNaNToFloat d - Double -> fromIntegral $ + Double -> fromIntegral $ castDoubleToWord64 d _ -> impossible "constFP" "copilot-bluespec" - ctype = case ty of + ctype = case ty of Float -> "Bit 32" Double -> "Bit 64" _ -> impossible "constFP" "copilot-bluespec" in BS.CApply (BS.CVar $ BS.mkId BS.NoPos "unpack") [cexpr] - | d == 0 = BS.CApply - (BS.CVar $ BS.mkId BS.NoPos "zero") - [ if isNegativeZero d - then BS.CCon BS.idTrue [] + | d == 0 = BS.CApply + (BS.CVar $ BS.mkId BS.NoPos "zero") + [ if isNegativeZero d + then BS.CCon BS.idTrue [] else BS.CCon BS.idFalse [] ] | d > 0 = withTypeAnnotation ty $ cLit $ BS.LReal d @@ -676,13 +676,13 @@ constFP ty d [cLit $ BS.LReal $ negate d] -- Because NaNs are specially placed as bit-representations by `constfp` and `constfp` requires Doubles as inputs, --- we need to convert NaN doubles safely into NaN floats. GHC Double2Float does not preserve signal nor payload +-- we need to convert NaN doubles safely into NaN floats. GHC Double2Float does not preserve signal nor payload -- internals of NaNs so in this case we need a special conversion. uncastDoubleNaNToFloat :: Double -> Float -uncastDoubleNaNToFloat nan = if (isSignaling nan) - then (setPayloadSignaling payload) +uncastDoubleNaNToFloat nan = if (isSignaling nan) + then (setPayloadSignaling payload) else (setPayload payload) - where + where payload = double2Float $ getPayload nan -- | Create a Bluespec expression consisting of a literal. From 78cdf022d05c422358782bcf19fe210d5d69188b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 14:04:03 -0700 Subject: [PATCH 437/483] copilot-bluespec: Document changes in CHANGELOG. Refs #775. --- copilot-bluespec/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index aa122183f..b3cc16e04 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,5 +1,6 @@ 2026-10-12 * Replace tabs in `DESIGN.md`. (#772) + * Remove trailing spaces. (#775) 2026-09-07 * Version bump (4.8.1). (#760) From 64912e909a1023d252e2196eba881fd1e66907dc Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 18:29:33 -0700 Subject: [PATCH 438/483] copilot: Adjust Travis CI script to flag trailing space as an error. Refs #777. The current CI settings do not detect if trailing space has been introduced in source code, documentation or CHANGELOGs. That lack of automation makes the accidental introduction of trailing space in the code more likely, especially by new users who have not yet configured their editor to automatically remove trailing spaces. To facilitate keeping Copilot conformant with the style guide, we want to automatically detect when trailing spaces are introduced and warn users accordingly. This commit modifies the Travis CI script to stop with an error if any non-binary files contain any trailing spaces. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index de3f14bb5..6cf023165 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,9 @@ before_install: - git submodule update --remote script: + # Fail if the any non-binary files contain trailing spaces. + - if grep -nIHre ' \+$' * ; then exit 1; fi + # Fail if the code, Cabal files, documentation or CHANGELOGs contain tabs. - if grep -rP '\t' --include='*.hs' --include='*.cabal' --include='*.md' --include='CHANGELOG' . ; then exit 1; fi From 177982b2d4cd653774ceb8fae714126f6a4ce3c9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Mon, 12 Oct 2026 18:32:05 -0700 Subject: [PATCH 439/483] copilot: Document changes in CHANGELOG. Refs #777. --- copilot/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index e7b1e3700..f28d79c55 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,5 +1,6 @@ 2026-10-12 * Adjust Travis CI script to flag presence of tabs as an error. (#774) + * Adjust Travis CI script to flag trailing space as an error. (#777) 2026-09-07 * Version bump (4.8.1). (#760) From 2a04a921cff2f492323512fbd9bf6ade6172a7af Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:14:33 -0700 Subject: [PATCH 440/483] copilot: Use "into" instead of "in" when indicating movement in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit replaces a use of "in" with "into" after a verb that denotes movement or change with direction, which improves the style of the sentence. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 893d6a933..a1f7fd875 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -7,7 +7,7 @@ Copilot is a runtime verification framework for hard real-time systems. Programs can be interpreted for testing, or translated into C99 code to be -incorporated in a project or standalone application. The C99 code generated +incorporated into a project or standalone application. The C99 code generated is constant in memory and time, making it suitable for systems with hard real-time requirements. From e76d6534a876d71075d885678c019bf6d2f7cd93 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:13:31 -0700 Subject: [PATCH 441/483] copilot: Add missing article in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit adds a missing article in a sentence, part of the list of features of Copilot, which is grammatically more correct and reads better. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index a1f7fd875..2d8d4ed5a 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -26,7 +26,7 @@ real-time requirements. - Produce hard real-time C99 runtime monitors that run in constant memory and time. -- Catch errors in specifications early using expressive static type system. +- Catch errors in specifications early using an expressive static type system. - Prove properties about specifications using theorem proving extensions. From e351df801b08221074bb0a65d407b279c86b4faf Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:14:49 -0700 Subject: [PATCH 442/483] copilot: Add missing comma in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit adds a comma after the introductory phrase "to use Copilot". --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 2d8d4ed5a..49bb8358c 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -98,7 +98,7 @@ ghci> ghci> Leaving GHCi. ### Other Linux distributions On other Linux distributions or older Debian-based distributions, to use -Copilot you must install a Haskell compiler (GHC) and the package manager +Copilot, you must install a Haskell compiler (GHC) and the package manager Cabal. We currently support all versions of GHC from 8.6.5 to modern versions (9.10 as of this writing). You can install the toolchain using [ghcup](https://www.haskell.org/ghcup/) or, if you are on Debian/Ubuntu, From 73a3444671661c9e83410f8c18aab671c55421ed Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:14:58 -0700 Subject: [PATCH 443/483] copilot: Add missing comma in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit adds a comma after the introductory phrase "To use Copilot". --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 49bb8358c..0cebe449d 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -129,7 +129,7 @@ ghci> ghci> Leaving GHCi. ## Mac installation [(Back to top)](#table-of-contents) -To use Copilot you must have a Haskell compiler (GHC) and the package manager +To use Copilot, you must have a Haskell compiler (GHC) and the package manager Cabal. We currently support all versions of GHC from 8.6.5 to modern versions (9.6 as of this writing). You can install the toolchain using [ghcup](https://www.haskell.org/ghcup/), as well as with Homebrew: From c05161555f1714d1d913ebabde3e87c1373c75fb Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 14 Oct 2026 20:21:00 -0700 Subject: [PATCH 444/483] copilot: Fix spelling of Travis CI in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit fixes the spelling of "Travis CI". --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 0cebe449d..03ffcf650 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -162,7 +162,7 @@ ghci> ghci> Leaving GHCi. Feel free to open an issue if you are unable to install Copilot following these instructions. -There is a TravisCI file at the root of the repository that may help with +There is a Travis CI file at the root of the repository that may help with troubleshooting the installation. Our issues often include comments with Dockerfiles listing the steps necessary to install Copilot from scratch. From cc42fd662cd6a423f37ffadd892d9c349e5dad5d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:13:37 -0700 Subject: [PATCH 445/483] copilot: Fix discrepancy between pronoun and verb in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit fixes a discrepancy between the number of the pronoun in a sentence ("It") and the verb ("read"). It also adds an article accmpanying temperature, since there is no marker suggesting it refers specifically to the global C variable by that name. --- copilot/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/copilot/README.md b/copilot/README.md index 03ffcf650..ec2c3723b 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -176,8 +176,9 @@ of the main repository. ```haskell -- This example implements a simple home heating system. The system heats --- when the temperature gets too low, and stops when it is high enough. It read --- temperature as a byte (range -50C to 100C) and translates this to Celsius. +-- when the temperature gets too low, and stops when it is high enough. It +-- reads the temperature as a byte (range -50C to 100C) and translates it to +-- Celsius. module Heater where From f32cf04d3e4b9af0ea875c1402b476061143aaa2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:18:34 -0700 Subject: [PATCH 446/483] copilot: Fix capitalization of Copilot in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit fixes the capitalization of Copilot referring to the project. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index ec2c3723b..66060e7ca 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -239,7 +239,7 @@ does not constitute any form of endorsement._ built using NASA's Core Flight System or the Robot Operating System (ROS 2). - [arduino-copilot](https://hackage.haskell.org/package/arduino-copilot) - facilitates building copilot applications that run on Arduino. + facilitates building Copilot applications that run on Arduino. - [sketch-frp-copilot](https://hackage.haskell.org/package/sketch-frp-copilot) extends Copilot with an FRP-like interface. From 5d879d850d14d1e8c263cffa6a2759bdd68f7a35 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:18:43 -0700 Subject: [PATCH 447/483] copilot: Fix capitalization of Copilot in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit fixes the capitalization of Copilot referring to the project. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 66060e7ca..7332fd7da 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -245,7 +245,7 @@ does not constitute any form of endorsement._ extends Copilot with an FRP-like interface. - [zephyr-copilot](https://hackage.haskell.org/package/zephyr-copilot) - facilitates building copilot applications that run on boards supported by the + facilitates building Copilot applications that run on boards supported by the Zephyr project. # Documentation From dc3ac33be460111f241e3bec7d3870c791f5e173 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Wed, 14 Oct 2026 20:27:36 -0700 Subject: [PATCH 448/483] copilot: Add missing comma in README. Refs #779. The README contains several typos, missing commas, issues with capitalization, and poor wording in some sentences. For example, Copilot is written as `Copilot` in some places, but `copilot` in others, even when referring to the project (as opposed to the library). The sentence "It read temperature as a byte", which accompanies a code example, is grammatically incorrect. To make the README useful and easy to read, we should fix all syntactic and grammatical errors wherever possible. This commit adds a missing comma after an introductory adverb. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 7332fd7da..536665d87 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -391,5 +391,5 @@ https://github.com/Copilot-Language/copilot/graphs/contributors We are grateful for NASA Contract NNL08AD13T to Galois, Inc. and the National Institute of Aerospace, which partially supported this work. -Additionally NASA Langley contracts 80LARC17C0004 and NNL09AA00A supported +Additionally, NASA Langley contracts 80LARC17C0004 and NNL09AA00A supported further development of Copilot. From 12024c6efe5d80bb09d52eebfb78d23b8536a4ee Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Thu, 15 Oct 2026 22:12:30 -0700 Subject: [PATCH 449/483] copilot: Document changes in CHANGELOG. Refs #779. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index f28d79c55..4758ed15b 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,6 +1,7 @@ -2026-10-12 +2026-10-15 * Adjust Travis CI script to flag presence of tabs as an error. (#774) * Adjust Travis CI script to flag trailing space as an error. (#777) + * Update README to correct typos and improve readability. (#779) 2026-09-07 * Version bump (4.8.1). (#760) From 7c2cbbd99c2bf3d476c2b493f0127a61252a4566 Mon Sep 17 00:00:00 2001 From: Kaveh Zare Date: Fri, 16 Oct 2026 13:30:21 -0500 Subject: [PATCH 450/483] copilot-core: Fix comments for two tests in `Test.Copilot.Core.Type`. Refs #781. The Haddock comments that accompany the properties `testUpdateStruct` and `testUpdateStruct`, included in the test module `copilot-core:Test.Copilot.Core.Type` are incorrect. The comments indicate that the properties test showing a struct, when they are related to updates instead. All Haddock comments should correctly document the functionality implemented in Copilot. This commit fixes the Haddock comments for the aforementioned test properties. --- copilot-core/tests/Test/Copilot/Core/Type.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type.hs b/copilot-core/tests/Test/Copilot/Core/Type.hs index 9f9cb6d5e..128cdea67 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type.hs @@ -350,7 +350,7 @@ testShowStruct = forAll arbitrary $ \k1 -> forAll arbitrary $ \k2 -> show (S (Field k1) (Field k2)) == "" --- | Test showing a struct. +-- | Test updating a struct. testUpdateStruct :: Property testUpdateStruct = forAll arbitrary $ \k1 -> @@ -364,7 +364,7 @@ testUpdateStruct = where unField (Field x) = x --- | Test showing a struct. +-- | Test updating a struct (should fail). testUpdateStructFail :: Property testUpdateStructFail = expectFailure $ forAll arbitrary $ \k1 -> From 54f18ef8b755704082929a374ab8700c61eaeb9c Mon Sep 17 00:00:00 2001 From: Kaveh Zare Date: Fri, 16 Oct 2026 13:35:12 -0500 Subject: [PATCH 451/483] copilot-core: Document changes in CHANGELOG. Refs #781. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index 8ffeafc40..ad519557c 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,7 +1,8 @@ -2026-10-12 +2026-10-16 * Increase test coverage for `Copilot.Core.Type.Array`. (#767) * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) * Replace tab in CHANGELOG. (#772) + * Fix comments for two tests in `Test.Copilot.Core.Type`. (#781) 2026-09-07 * Version bump (4.8.1). (#760) From 570cda7fa751149911d0e69f6ab1d9a57b48ccb2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 17 Oct 2026 08:20:18 -0700 Subject: [PATCH 452/483] copilot-core: Fix typo in Haddock comment of test property. Refs #783. The Haddock comment for the property `copilot-core:Test.Copilot.Core.Type.testTypeLength2` states that it tests "that the length of an multi-dimensional array is as expected", which is grammatically incorrect. To make documentation most useful and in compliance with our style guide, the documentation should have no typos or grammatical errors. This commit fixes the aforementioned typo. --- copilot-core/tests/Test/Copilot/Core/Type.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type.hs b/copilot-core/tests/Test/Copilot/Core/Type.hs index 128cdea67..979f87d88 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type.hs @@ -165,7 +165,7 @@ testTypeLength1 = property $ typeLength a == 3 a :: Type (Array 3 Int8) a = Array Int8 --- | Test that the length of an multi-dimensional array is as expected. +-- | Test that the length of a multi-dimensional array is as expected. testTypeLength2 :: Property testTypeLength2 = property $ typeLength a == 3 where From 7c9c43fe28bd2cf4d311547fcadf372c646f134e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 18 Oct 2026 09:28:29 -0700 Subject: [PATCH 453/483] copilot-core: Document changes in CHANGELOG. Refs #783. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index ad519557c..b2d405f95 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,8 +1,9 @@ -2026-10-16 +2026-10-17 * Increase test coverage for `Copilot.Core.Type.Array`. (#767) * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) * Replace tab in CHANGELOG. (#772) * Fix comments for two tests in `Test.Copilot.Core.Type`. (#781) + * Fix typo in Haddock comment of test property. (#783) 2026-09-07 * Version bump (4.8.1). (#760) From 6046703392ad20ea306b16ab03a7d604a68c0af6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 17 Oct 2026 08:30:23 -0700 Subject: [PATCH 454/483] copilot-core: Fix test to use `typeSize` instead of `typeLength`. Refs #784. The test property `copilot-core:Test.Copilot.Core.Type.testTypeSize1` claims, in its name, in the Haddock comment that accompanies it, and in the property description as part of the property group, that it tests `typeSize`. However, its implementation tests `typeLength`. The property should test the function it is expected to be testing. This commit modifies `copilot-core:Test.Copilot.Core.Type.testTypeSize1` to use `typeSize` instead of `typeLength`. --- copilot-core/tests/Test/Copilot/Core/Type.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/tests/Test/Copilot/Core/Type.hs b/copilot-core/tests/Test/Copilot/Core/Type.hs index 979f87d88..47d8c5940 100644 --- a/copilot-core/tests/Test/Copilot/Core/Type.hs +++ b/copilot-core/tests/Test/Copilot/Core/Type.hs @@ -174,7 +174,7 @@ testTypeLength2 = property $ typeLength a == 3 -- | Test that the size of an array is as expected. testTypeSize1 :: Property -testTypeSize1 = property $ typeLength a == 3 +testTypeSize1 = property $ typeSize a == 3 where a :: Type (Array 3 Int8) a = Array Int8 From 1aac617ad5ac27e167ff755e5905c1b0e1c507e6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sun, 18 Oct 2026 11:43:35 -0700 Subject: [PATCH 455/483] copilot-core: Document changes in CHANGELOG. Refs #784. --- copilot-core/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index b2d405f95..c8a3f790d 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -4,6 +4,7 @@ * Replace tab in CHANGELOG. (#772) * Fix comments for two tests in `Test.Copilot.Core.Type`. (#781) * Fix typo in Haddock comment of test property. (#783) + * Fix test to use `typeSize` instead of `typeLength` (#784). 2026-09-07 * Version bump (4.8.1). (#760) From 5c32631cb72d51ce8b91802f58b840e2f482d05f Mon Sep 17 00:00:00 2001 From: James Todd Gloyd Date: Fri, 23 Oct 2026 20:54:08 +0000 Subject: [PATCH 456/483] copilot-libraries: Add random stream generation module. Refs #766. Copilot does not currently contain any capability to produce a random stream of values. Randomization is useful for a lot of applications, from testing to fault injection. To make Copilot more versatile and useful for the use cases we want to support, we want to add a module to `copilot-libraries` with randomization primitives. This commit adds a new module that provides functions to produce random streams of different types and with different properties. --- copilot-libraries/copilot-libraries.cabal | 1 + .../src/Copilot/Library/Random.hs | 145 ++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 copilot-libraries/src/Copilot/Library/Random.hs diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index aa5c8e61b..b34020a57 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -51,6 +51,7 @@ library , Copilot.Library.PTLTL , Copilot.Library.StateMachines , Copilot.Library.Statistics + , Copilot.Library.Random , Copilot.Library.RegExp , Copilot.Library.Utils , Copilot.Library.Voting diff --git a/copilot-libraries/src/Copilot/Library/Random.hs b/copilot-libraries/src/Copilot/Library/Random.hs new file mode 100644 index 000000000..d4828e12b --- /dev/null +++ b/copilot-libraries/src/Copilot/Library/Random.hs @@ -0,0 +1,145 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +-- | +-- Basic pseudo-random number generation. +-- +-- Information on performance of a selection of pseudo-random number generation +-- algorithms, including those implemented below, can be found at: +-- https://prng.di.unimi.it/ +-- +-- Important: these pseudo-random number generators are NOT cryptographically +-- secure generators. +module Copilot.Library.Random + ( randomW32 + , randomW64 + , boundedRandomIntegral + , proportionalRandomBool + , weightedRandomSelection + ) + where + +-- External imports +import Copilot.Language (Array, Stream, Typed, Word32, Word64, arrayElems, + constant, mod, mux, (!), (++), (.<<.), (.>>.), (.^.), + (<), (<=)) +import GHC.TypeLits (KnownNat, type (<=)) +import Prelude hiding (mod, (++), (<), (<=)) + +-- | Given two Word32 seeds, produce a pseudo-random stream of Word32 numbers, +-- which has a period of 2^32 - 1. Neither of the seeds should be zero, since +-- the underlying randomization algorithm maps zero to itself. +-- +-- This function uses an Xorshift+ algorithm. +randomW32 :: Word32 -> Word32 -> Stream Word32 +randomW32 0 _ = error "randomW32: seed cannot be zero." +randomW32 _ 0 = error "randomW32: seed cannot be zero." +randomW32 seed1 seed2 = s1 + s2 + + where + + s1 :: Stream Word32 + s1 = [seed1] ++ (xorshift32 s1) + + s2 :: Stream Word32 + s2 = [seed2] ++ (xorshift32 s2) + + -- This is a simple implementation of an Xorshift algorithm + xorshift32 :: Stream Word32 -> Stream Word32 + xorshift32 x1 = x4 -- Note: 0 maps to itself with this + where + x4 = x3 .^. (x3 .<<. (5 :: Stream Word32)) + x3 = x2 .^. (x2 .>>. (17 :: Stream Word32)) + x2 = x1 .^. (x1 .<<. (13 :: Stream Word32)) + +-- | Given two Word64 seeds, produce a pseudo-random stream of Word64 numbers, +-- which has a period of 2^128 - 1. Neither of the seeds should be zero, as +-- doing so will negatively impact the quality of the randomness. +-- +-- This function uses an Xorshiftrplus algorithm. +randomW64 :: Word64 -> Word64 -> Stream Word64 +randomW64 0 _ = error "randomW64: seed cannot be zero." +randomW64 _ 0 = error "randomW64: seed cannot be zero." +randomW64 seed1 seed2 = x + + where + + s0 :: Stream Word64 + s0 = [seed1] ++ s1 + + s1 :: Stream Word64 + s1 = [seed2] ++ (x + s1) + + x :: Stream Word64 + x = xorshiftr128plus s0 s1 + + xorshiftr128plus :: Stream Word64 -> Stream Word64 -> Stream Word64 + xorshiftr128plus x1 y = x4 + where + x4 = x3 .^. y + x3 = x2 .^. (x2 .>>. (17 :: Stream Word64)) + x2 = x1 .^. (x1 .<<. (23 :: Stream Word64)) + +-- | Given a stream of random integral numbers, a minimum value, and a maximum +-- value, produce a stream of random values greater than or equal to the +-- minimum and less than the maximum value, i.e., [@mN@, @mX@). +boundedRandomIntegral :: (Typed a, Integral a) + => Stream a -> a -> a -> Stream a +boundedRandomIntegral gen mN mX = (gen `mod` constant (mX - mN)) + constant mN + +-- | Given a stream of random integral numbers and two integers defining the +-- numerator and denominator of a rational probability, produce a boolean +-- stream that is randomly @True@ with the given probability. That is to say, +-- given a numerator @n@ and denominator @d@, then, over a sufficiently large +-- sample size, the resulting stream @s@ is @True@ @n@ out of @d@ times. +proportionalRandomBool :: (Typed a, Integral a) + => Stream a -> a -> a -> Stream Bool +proportionalRandomBool gen n d = (gen `mod` constant d) < constant n + +-- | Given a stream of random integral numbers, an array of values, and an +-- equal length array of weights, produce a stream of random values from the +-- given array with probabilities proportional to the given weights. +weightedRandomSelection :: forall a b s + . (KnownNat s, s <= MaxWord32, Typed a, Typed b, + Integral a, Bounded a) + => Stream a -> Array s b -> Array s a -> Stream b +weightedRandomSelection gen values weights + | overflow + = error "weightedRandomSelection: sum of weights overflows type" + + | signedType + = error "weightedRandomSelection: type of weights array must be unsigned" + + | otherwise + = constant values ! idx + + where + + -- Check if cumulativeSum decreases at any point. For unsigned integral + -- types, a decreasing element in this cumulative sequence corresponds to + -- an overflow. + overflow = or (zipWith (>) cumulativeSum (drop 1 cumulativeSum)) + + signedType = 0 > (minBound :: a) + + idx :: Stream Word32 + idx = countLeq cumulativeSum (gen `mod` constant totalSum) + + countLeq :: [a] -> Stream a -> Stream Word32 + countLeq xs v = sum [ mux (constant x <= v) 1 0 | x <- xs ] + -- ^ get number of elements in @xs@ less than or equal to @v@. + -- For non-decreasing @xs@, this is equal to the index of the first + -- element greater than @v@. + + cumulativeSum :: [a] + cumulativeSum = scanl1 (+) (arrayElems weights) + + totalSum :: a + totalSum = last cumulativeSum + +-- | Type-level representation of the upper bound on an unsigned 32-bit +-- integer. +type MaxWord32 = 4294967295 -- 2^32 - 1 From 39548759d483719d4554bf8cd2e21adf5a8b21aa Mon Sep 17 00:00:00 2001 From: James Todd Gloyd Date: Fri, 23 Oct 2026 21:04:47 +0000 Subject: [PATCH 457/483] copilot-libraries: Document changes in CHANGELOG. Refs #766. --- copilot-libraries/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index 0e8dcc83f..b95a2cd3f 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,3 +1,6 @@ +2026-10-23 + * Add random stream generation module. (#766) + 2026-09-07 * Version bump (4.8.1). (#760) From e6d4b6450f8c90247d7d9f5b105d1cbb9217901d Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 8 May 2026 14:56:02 +0000 Subject: [PATCH 458/483] copilot: Add Github Actions workflow file. Refs #790. CI jobs are being automatically cancelled by Travis CI mid-build. Travis CI is reporting the plan as being out of credits, when the organization have plenty of credits to run jobs to completion. This commit adds a Github Actions workflow file that replicates the steps executed by Travis CI. --- .github/workflows/build.yml | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..21e929e6c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,66 @@ +name: build + +# Trigger the workflow on push or pull request +on: + - pull_request + - push + +jobs: + cabal: + name: ${{ matrix.os }} / ghc ${{ matrix.ghc }} / cabal ${{ matrix.cabal }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-26.04] + cabal: ["2.4", "3.2", "3.4"] + ghc: + - "8.6.5" + - "8.8.4" + - "8.10.4" + - "9.0.1" + + steps: + + - uses: haskell-actions/setup@main + id: setup-haskell-cabal + name: Setup Haskell + continue-on-error: true + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + + - name: Prepare environment + run: | + echo "$HOME/.ghcup/bin" >> $GITHUB_PATH + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + sed -i -e "s;-- \(secure.\) True;\1 False;g" $HOME/.cabal/config + cabal update + + - uses: actions/checkout@v7 + + - name: Install dependencies + run: | + sudo apt-get install -y cppcheck z3 + + - name: Format check + run: | + # Fail if the any non-binary files contain trailing spaces. + if grep -nIHre ' \+$' * ; then exit 1; fi + + # Fail if the code, Cabal files, documentation or CHANGELOGs contain tabs. + if grep -rP '\t' --include='*.hs' --include='*.cabal' --include='*.md' --include='CHANGELOG' . ; then exit 1; fi + + - name: Install copilot + run: | + cabal v2-install --lib copilot/ copilot-core/ copilot-c99/ copilot-language/ copilot-libraries/ copilot-theorem/ copilot-interpreter/ copilot-prettyprinter/ + + - name: Test packages + if: matrix.ghc == '8.10.4' + run: | + cabal v2-test -j1 copilot-core/ copilot-language/ copilot-interpreter/ copilot-c99/ copilot-theorem/ copilot-libraries/ + + - name: Check cpp + if: matrix.ghc == '8.10.4' + run: | + export GHC_ENVIRONMENT=~/.ghc/x86_64-linux-8.10.4/environments/default + runhaskell copilot/examples/Heater.hs; cppcheck --force --addon=misra.py --suppress=misra-c2012-14.4 --error-exitcode=2 heater.c From a39952269a8545c7e71002b53f632336794c4be3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Fri, 8 May 2026 14:56:13 +0000 Subject: [PATCH 459/483] copilot: Remove Travis CI file. Refs #790. CI jobs are being automatically cancelled by Travis CI mid-build. Travis CI is reporting the plan as being out of credits, when the organization have plenty of credits to run jobs to completion. A prior commit added a Github Actions workflow file that replicates the steps executed by Travis CI, thus making the Travis CI setup redundant. This commit removes the Travis CI script from the repository. --- .travis.yml | 58 ----------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6cf023165..000000000 --- a/.travis.yml +++ /dev/null @@ -1,58 +0,0 @@ -# dist should be at least focal because we need cppcheck >= 1.88. -dist: focal - -# NB: don't set `language: haskell` here - -# The following enables several GHC versions to be tested; often it's enough to -# test only against the last release in a major GHC version. Feel free to omit -# lines listings versions you don't need/want testing for. -env: - - CABALVER=2.4 GHCVER=8.6.5 - - CABALVER=3.2 GHCVER=8.8.4 - - CABALVER=3.2 GHCVER=8.10.4 - - CABALVER=3.4 GHCVER=9.0.1 -# - CABALVER=head GHCVER=head # see section about GHC HEAD snapshots - -# Note: the distinction between `before_install` and `install` is not important. -before_install: - - travis_retry sudo add-apt-repository -y ppa:hvr/ghc - - travis_retry sudo apt-get update - - travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex - - # We install z3 and cppcheck only for the tests, since they are not needed - # for normal compilation. - - if [ "${GHCVER}" == "8.10.4" ]; then travis_retry sudo apt-get install --yes z3 cppcheck; fi - - - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH - - cabal --version - - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" - - cabal user-config init - - sed -i -e "s;-- \(secure.\) True;\1 False;g" ${HOME}/.cabal/config - - travis_retry cabal update - - git submodule update --remote - -script: - # Fail if the any non-binary files contain trailing spaces. - - if grep -nIHre ' \+$' * ; then exit 1; fi - - # Fail if the code, Cabal files, documentation or CHANGELOGs contain tabs. - - if grep -rP '\t' --include='*.hs' --include='*.cabal' --include='*.md' --include='CHANGELOG' . ; then exit 1; fi - - # We explicitly install all libraries so that they are exposed and we can use - # them for tests (e.g., with runhaskell). There is no harm in doing this - # instead of installing just copilot. - - travis_wait 30 cabal v2-install --lib copilot copilot-core copilot-c99 copilot-language copilot-libraries copilot-theorem copilot-interpreter copilot-prettyprinter - - # Run tests only on GHC 8.10.4 - # - # Only libraries with tests are listed below or the v2-test command fails. - # - # Testing copilot-theorem and copilot-libraries requires z3. See above - # conditional installation, and keep GHC version numbers in both places in - # sync. - - if [ "${GHCVER}" == "8.10.4" ]; then cabal v2-test -j1 copilot-core copilot-language copilot-interpreter copilot-c99 copilot-theorem copilot-libraries; fi - - # Check that the code produced by Copilot complies with MISRA C 2012. We - # explicitly make cppcheck produce a non-zero exit code on non-compliance - # with the standard to make the CI build fail. - - if [ "${GHCVER}" == "8.10.4" ]; then runhaskell copilot/examples/Heater.hs; cppcheck --force --addon=misra.py --suppress=misra-c2012-14.4 --error-exitcode=2 heater.c; fi From b0e6e698ded581bdb49c7d7e0ac6196e39cab2b8 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 21:01:56 +0000 Subject: [PATCH 460/483] copilot: Replace Travis CI badge with Github Actions badge in README. Refs #790. CI jobs are being automatically cancelled by Travis CI mid-build. Travis CI is reporting the plan as being out of credits, when the organization have plenty of credits to run jobs to completion. Prior commits replaced the Travis CI-based setup with one based on Github Actions. This commit updates the Travis CI build status badge in the `README.md` to reflect the build status of the `master` branch on Github Actions, and to point to the corresponding workflow file. --- copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot/README.md b/copilot/README.md index 536665d87..63a2459ef 100644 --- a/copilot/README.md +++ b/copilot/README.md @@ -2,7 +2,7 @@ # Copilot -[![Build Status](https://api.travis-ci.com/Copilot-Language/copilot.svg?branch=master)](https://app.travis-ci.com/github/Copilot-Language/copilot) +[![Build Status](https://github.com/Copilot-Language/copilot/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Copilot-Language/copilot/actions/workflows/build.yml) [![Version on Hackage](https://img.shields.io/hackage/v/copilot.svg)](https://hackage.haskell.org/package/copilot) Copilot is a runtime verification framework for hard real-time systems. From 701880b127fadd9d94f9f8c73ec44ba791d42775 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 21:27:02 +0000 Subject: [PATCH 461/483] copilot: Document changes in CHANGELOG. Refs #790. --- copilot/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index 4758ed15b..c350ec33a 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,7 +1,8 @@ -2026-10-15 +2026-11-07 * Adjust Travis CI script to flag presence of tabs as an error. (#774) * Adjust Travis CI script to flag trailing space as an error. (#777) * Update README to correct typos and improve readability. (#779) + * Replace Travis CI with Github Actions. (#790) 2026-09-07 * Version bump (4.8.1). (#760) From 1a137a86d5a8459750d3092e28679cb326e868d2 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 462/483] copilot-core: Version bump (4.8.2). Refs #792. --- copilot-core/copilot-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-core/copilot-core.cabal b/copilot-core/copilot-core.cabal index 57f92d703..288399a9c 100644 --- a/copilot-core/copilot-core.cabal +++ b/copilot-core/copilot-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core -version: 4.8.1 +version: 4.8.2 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot. From c666b71483eaf5889f53fa82d588d991bc123e35 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 463/483] copilot-bluespec: Version bump (4.8.2). Refs #792. --- copilot-bluespec/copilot-bluespec.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-bluespec/copilot-bluespec.cabal b/copilot-bluespec/copilot-bluespec.cabal index e8f29e4d7..1d8f7b3af 100644 --- a/copilot-bluespec/copilot-bluespec.cabal +++ b/copilot-bluespec/copilot-bluespec.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-bluespec -version : 4.8.1 +version : 4.8.2 synopsis : A compiler for Copilot targeting FPGAs. description : This package is a back-end from Copilot to FPGAs in Bluespec. @@ -51,7 +51,7 @@ library , ieee754 >= 0.8.0 && < 0.9 , pretty >= 1.1.2 && < 1.2 - , copilot-core >= 4.8.1 && < 4.9 + , copilot-core >= 4.8.2 && < 4.9 , language-bluespec >= 0.1 && < 0.2 exposed-modules : Copilot.Compile.Bluespec From fd210f80474c58044014aaa8bf5014aeffa1ef16 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 464/483] copilot-c99: Version bump (4.8.2). Refs #792. --- copilot-c99/copilot-c99.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index ea2e233dc..7e1e3d17a 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -1,6 +1,6 @@ cabal-version : >= 1.10 name : copilot-c99 -version : 4.8.1 +version : 4.8.2 synopsis : A compiler for Copilot targeting C99. description : This package is a back-end from Copilot to C. @@ -45,7 +45,7 @@ library , mtl >= 2.2 && < 2.4 , pretty >= 1.1 && < 1.2 - , copilot-core >= 4.8.1 && < 4.9 + , copilot-core >= 4.8.2 && < 4.9 , language-c99 >= 0.2.0 && < 0.3 , language-c99-simple >= 0.3 && < 0.4 From c2b0269f9838f21e08e040e4bcb39635fd683eb0 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 465/483] copilot-prettyprinter: Version bump (4.8.2). Refs #792. --- copilot-prettyprinter/copilot-prettyprinter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-prettyprinter/copilot-prettyprinter.cabal b/copilot-prettyprinter/copilot-prettyprinter.cabal index d6c2bcc11..35c5c7d7c 100644 --- a/copilot-prettyprinter/copilot-prettyprinter.cabal +++ b/copilot-prettyprinter/copilot-prettyprinter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-prettyprinter -version: 4.8.1 +version: 4.8.2 synopsis: A prettyprinter of Copilot Specifications. description: A prettyprinter of Copilot specifications. @@ -45,7 +45,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.8.1 && < 4.9 + copilot-core >= 4.8.2 && < 4.9 exposed-modules: From d17de87e12bc1a7ba2f4bf4e26ec83e03d768c81 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 466/483] copilot-interpreter: Version bump (4.8.2). Refs #792. --- copilot-interpreter/copilot-interpreter.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-interpreter/copilot-interpreter.cabal b/copilot-interpreter/copilot-interpreter.cabal index 538237a10..60152a66a 100644 --- a/copilot-interpreter/copilot-interpreter.cabal +++ b/copilot-interpreter/copilot-interpreter.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-interpreter -version: 4.8.1 +version: 4.8.2 synopsis: Interpreter for Copilot. description: Interpreter for Copilot. @@ -44,7 +44,7 @@ library base >= 4.9 && < 5, pretty >= 1.0 && < 1.2, - copilot-core >= 4.8.1 && < 4.9 + copilot-core >= 4.8.2 && < 4.9 exposed-modules: From 8e714c714950405f568b5e2ebb2f1a3e15ebc04e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 467/483] copilot-theorem: Version bump (4.8.2). Refs #792. --- copilot-theorem/copilot-theorem.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/copilot-theorem/copilot-theorem.cabal b/copilot-theorem/copilot-theorem.cabal index cf3e8c44c..da29f566a 100644 --- a/copilot-theorem/copilot-theorem.cabal +++ b/copilot-theorem/copilot-theorem.cabal @@ -14,7 +14,7 @@ description: . -version : 4.8.1 +version : 4.8.2 license : BSD3 license-file : LICENSE maintainer : Ivan Perez @@ -63,8 +63,8 @@ library , transformers >= 0.5 && < 0.7 , what4 >= 1.3 && < 1.8 - , copilot-core >= 4.8.1 && < 4.9 - , copilot-prettyprinter >= 4.8.1 && < 4.9 + , copilot-core >= 4.8.2 && < 4.9 + , copilot-prettyprinter >= 4.8.2 && < 4.9 exposed-modules : Copilot.Theorem , Copilot.Theorem.Prove From 1f67ea0f33b69c775f25dd21d48c540d95e0ac45 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 468/483] copilot-language: Version bump (4.8.2). Refs #792. --- copilot-language/copilot-language.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-language/copilot-language.cabal b/copilot-language/copilot-language.cabal index b30c29db6..40ba65f2c 100644 --- a/copilot-language/copilot-language.cabal +++ b/copilot-language/copilot-language.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-language -version: 4.8.1 +version: 4.8.2 synopsis: A Haskell-embedded DSL for monitoring hard real-time distributed systems. description: @@ -42,9 +42,9 @@ library , data-reify >= 0.6 && < 0.7 , mtl >= 2.0 && < 3 - , copilot-core >= 4.8.1 && < 4.9 - , copilot-interpreter >= 4.8.1 && < 4.9 - , copilot-theorem >= 4.8.1 && < 4.9 + , copilot-core >= 4.8.2 && < 4.9 + , copilot-interpreter >= 4.8.2 && < 4.9 + , copilot-theorem >= 4.8.2 && < 4.9 exposed-modules: Copilot.Language , Copilot.Language.Operators.BitWise From c97769b18999b8be9b673726817123466bf1c4e9 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 469/483] copilot-libraries: Version bump (4.8.2). Refs #792. --- copilot-libraries/copilot-libraries.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copilot-libraries/copilot-libraries.cabal b/copilot-libraries/copilot-libraries.cabal index b34020a57..6e18aba84 100644 --- a/copilot-libraries/copilot-libraries.cabal +++ b/copilot-libraries/copilot-libraries.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-libraries -version: 4.8.1 +version: 4.8.2 synopsis: Libraries for the Copilot language. description: Libraries for the Copilot language. @@ -41,7 +41,7 @@ library , containers >= 0.4 && < 0.9 , mtl >= 2.0 && < 2.4 , parsec >= 2.0 && < 3.2 - , copilot-language >= 4.8.1 && < 4.9 + , copilot-language >= 4.8.2 && < 4.9 exposed-modules: Copilot.Library.Analysis From dabb5562b912ae11074e2db5a3610c5a9c296bb6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 470/483] copilot-verifier: Version bump (4.8.2). Refs #792. --- copilot-verifier/copilot-verifier.cabal | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/copilot-verifier/copilot-verifier.cabal b/copilot-verifier/copilot-verifier.cabal index 4052ee006..bcf1061bf 100644 --- a/copilot-verifier/copilot-verifier.cabal +++ b/copilot-verifier/copilot-verifier.cabal @@ -1,6 +1,6 @@ Cabal-version: 2.2 Name: copilot-verifier -Version: 4.8.1 +Version: 4.8.2 Author: Galois Inc. Maintainer: rscott@galois.com Copyright: (c) Galois, Inc 2021-2024 @@ -45,9 +45,9 @@ common bldflags bv-sized >= 1.0.0 && < 1.1, bytestring, containers >= 0.5.9.0, - copilot-c99 >= 4.8.1 && < 4.9, - copilot-core >= 4.8.1 && < 4.9, - copilot-theorem >= 4.8.1 && < 4.9, + copilot-c99 >= 4.8.2 && < 4.9, + copilot-core >= 4.8.2 && < 4.9, + copilot-theorem >= 4.8.2 && < 4.9, crucible >= 0.7.1 && < 0.8, crucible-llvm >= 0.7 && < 0.8, crux >= 0.7.1 && < 0.8, @@ -80,10 +80,10 @@ library copilot-verifier-examples hs-source-dirs: examples build-depends: case-insensitive, - copilot >= 4.8.1 && < 4.9, - copilot-language >= 4.8.1 && < 4.9, - copilot-libraries >= 4.8.1 && < 4.9, - copilot-prettyprinter >= 4.8.1 && < 4.9, + copilot >= 4.8.2 && < 4.9, + copilot-language >= 4.8.2 && < 4.9, + copilot-libraries >= 4.8.2 && < 4.9, + copilot-prettyprinter >= 4.8.2 && < 4.9, copilot-verifier exposed-modules: Copilot.Verifier.Examples From a9847388d62d2b725acf1c521ce5b31efc9af989 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 471/483] copilot-visualizer: Version bump (4.8.2). Refs #792. --- copilot-visualizer/copilot-visualizer.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-visualizer/copilot-visualizer.cabal b/copilot-visualizer/copilot-visualizer.cabal index 3b1d539eb..34d2289bd 100644 --- a/copilot-visualizer/copilot-visualizer.cabal +++ b/copilot-visualizer/copilot-visualizer.cabal @@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: copilot-visualizer -version: 4.8.1 +version: 4.8.2 synopsis: Visualizer for Copilot. description: Visualizer for Copilot. @@ -65,10 +65,10 @@ library text >= 1.2.3.1 && < 2.2, websockets >= 0.12.7 && < 0.14, - copilot >= 4.8.1 && < 4.9, - copilot-core >= 4.8.1 && < 4.9, - copilot-interpreter >= 4.8.1 && < 4.9, - copilot-language >= 4.8.1 && < 4.9 + copilot >= 4.8.2 && < 4.9, + copilot-core >= 4.8.2 && < 4.9, + copilot-interpreter >= 4.8.2 && < 4.9, + copilot-language >= 4.8.2 && < 4.9 exposed-modules: @@ -121,7 +121,7 @@ executable tikz-example build-depends: base >= 4.9 && < 5, - copilot >= 4.8.1 && < 4.9, + copilot >= 4.8.2 && < 4.9, copilot-visualizer if flag(examples) From 82d2e07f507e688a8ab5415a9b469787f84fa904 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:38:42 -0800 Subject: [PATCH 472/483] copilot: Version bump (4.8.2). Refs #792. --- copilot/copilot.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/copilot/copilot.cabal b/copilot/copilot.cabal index 5fa90035e..00a495e3f 100644 --- a/copilot/copilot.cabal +++ b/copilot/copilot.cabal @@ -1,5 +1,5 @@ name: copilot -version: 4.8.1 +version: 4.8.2 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE @@ -52,12 +52,12 @@ library , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6 - , copilot-core >= 4.8.1 && < 4.9 - , copilot-theorem >= 4.8.1 && < 4.9 - , copilot-language >= 4.8.1 && < 4.9 - , copilot-libraries >= 4.8.1 && < 4.9 - , copilot-c99 >= 4.8.1 && < 4.9 - , copilot-prettyprinter >= 4.8.1 && < 4.9 + , copilot-core >= 4.8.2 && < 4.9 + , copilot-theorem >= 4.8.2 && < 4.9 + , copilot-language >= 4.8.2 && < 4.9 + , copilot-libraries >= 4.8.2 && < 4.9 + , copilot-c99 >= 4.8.2 && < 4.9 + , copilot-prettyprinter >= 4.8.2 && < 4.9 exposed-modules: Language.Copilot, Language.Copilot.Main From 9a6a2d4ebc52fa54fb7f090624c44b7ca6c8797e Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 473/483] copilot-core: Document changes in CHANGELOG. Refs #792. --- copilot-core/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-core/CHANGELOG b/copilot-core/CHANGELOG index c8a3f790d..033129dbc 100644 --- a/copilot-core/CHANGELOG +++ b/copilot-core/CHANGELOG @@ -1,4 +1,5 @@ -2026-10-17 +2026-11-07 + * Version bump (4.8.2). (#792) * Increase test coverage for `Copilot.Core.Type.Array`. (#767) * Fix errors in the documentation of `Copilot.Core.Spec`. (#769) * Replace tab in CHANGELOG. (#772) From 8511f4801141ff2773a1787cb9d51e97e6523b33 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 474/483] copilot-bluespec: Document changes in CHANGELOG. Refs #792. --- copilot-bluespec/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-bluespec/CHANGELOG b/copilot-bluespec/CHANGELOG index b3cc16e04..b5130fa0b 100644 --- a/copilot-bluespec/CHANGELOG +++ b/copilot-bluespec/CHANGELOG @@ -1,4 +1,5 @@ -2026-10-12 +2026-11-07 + * Version bump (4.8.2). (#792) * Replace tabs in `DESIGN.md`. (#772) * Remove trailing spaces. (#775) From 3096f9356cc298409b2c2beb909f5cbdff38458b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 475/483] copilot-c99: Document changes in CHANGELOG. Refs #792. --- copilot-c99/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index 2caf70ef2..b53dcc838 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) From c8e5d846f393ff4c60c079c510a6e4ca3cbb7ec3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 476/483] copilot-prettyprinter: Document changes in CHANGELOG. Refs #792. --- copilot-prettyprinter/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-prettyprinter/CHANGELOG b/copilot-prettyprinter/CHANGELOG index 799783ce8..bbe715c47 100644 --- a/copilot-prettyprinter/CHANGELOG +++ b/copilot-prettyprinter/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) From 3b79a5b0213f85ca0dd3a904afa474a00f9562d3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 477/483] copilot-interpreter: Document changes in CHANGELOG. Refs #792. --- copilot-interpreter/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-interpreter/CHANGELOG b/copilot-interpreter/CHANGELOG index 1b88d8eb7..55cdec51c 100644 --- a/copilot-interpreter/CHANGELOG +++ b/copilot-interpreter/CHANGELOG @@ -1,4 +1,5 @@ -2026-10-12 +2026-11-07 + * Version bump (4.8.2). (#792) * Replace tab in `Copilot.Interpreter.Eval`. (#772) 2026-09-07 From c6dcc87d50db4d3d33ecc7755d4fc4476413401c Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 478/483] copilot-theorem: Document changes in CHANGELOG. Refs #792. --- copilot-theorem/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-theorem/CHANGELOG b/copilot-theorem/CHANGELOG index 251b49484..0207c5446 100644 --- a/copilot-theorem/CHANGELOG +++ b/copilot-theorem/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) * Remove unnecessary extension. (#733) From 184eaeaa68300f3a303cb92d678ead8ae89acc2b Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 479/483] copilot-language: Document changes in CHANGELOG. Refs #792. --- copilot-language/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-language/CHANGELOG b/copilot-language/CHANGELOG index 49162ceb0..4aacc9e2e 100644 --- a/copilot-language/CHANGELOG +++ b/copilot-language/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) From 1efa2d78fd4f4d40cadd36e0721b9020806cc202 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 480/483] copilot-libraries: Document changes in CHANGELOG. Refs #792. --- copilot-libraries/CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/copilot-libraries/CHANGELOG b/copilot-libraries/CHANGELOG index b95a2cd3f..f84c75a48 100644 --- a/copilot-libraries/CHANGELOG +++ b/copilot-libraries/CHANGELOG @@ -1,4 +1,5 @@ -2026-10-23 +2026-11-07 + * Version bump (4.8.2). (#792) * Add random stream generation module. (#766) 2026-09-07 From 090ff4e6dd4914cd10497452de6970d23f84c5b5 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 481/483] copilot-verifier: Document changes in CHANGELOG. Refs #792. --- copilot-verifier/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-verifier/CHANGELOG b/copilot-verifier/CHANGELOG index 1dec1cfd0..c69ccd708 100644 --- a/copilot-verifier/CHANGELOG +++ b/copilot-verifier/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) From 11c0601e795f40db74538cc0d52c41f2810ebdf3 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 482/483] copilot-visualizer: Document changes in CHANGELOG. Refs #792. --- copilot-visualizer/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copilot-visualizer/CHANGELOG b/copilot-visualizer/CHANGELOG index 9d772eca0..b578bedb9 100644 --- a/copilot-visualizer/CHANGELOG +++ b/copilot-visualizer/CHANGELOG @@ -1,3 +1,6 @@ +2026-11-07 + * Version bump (4.8.2). (#792) + 2026-09-07 * Version bump (4.8.1). (#760) * Fix inconsistent version bounds for `copilot` in Cabal file. (#749) From 8049dbb1e8aea3b123ca54be5a285bd1950ea2c6 Mon Sep 17 00:00:00 2001 From: Ivan Perez Date: Sat, 7 Nov 2026 17:41:22 -0800 Subject: [PATCH 483/483] copilot: Document changes in CHANGELOG. Refs #792. --- copilot/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/copilot/CHANGELOG b/copilot/CHANGELOG index c350ec33a..8b691705e 100644 --- a/copilot/CHANGELOG +++ b/copilot/CHANGELOG @@ -1,4 +1,5 @@ 2026-11-07 + * Version bump (4.8.2). (#792) * Adjust Travis CI script to flag presence of tabs as an error. (#774) * Adjust Travis CI script to flag trailing space as an error. (#777) * Update README to correct typos and improve readability. (#779)