Skip to content

Commit ca738f7

Browse files
Merge branch 'develop-misra-2012'. Close #472.
**Description** The code generated by Copilot is not fully compliant with MISRA C. At present, it complies with all but one rules, and all but two directives of MISRA C 2012. Due to the nature of this project and the environment where it is meant to be used, we want to have compliance with all MISRA C rules, and if possible with all MISRA C directives. Any deviations should be properly justified and documented. **Type** - Feature: Compliance with MISRA C 2012. **Additional context** None. **Requester** - Patrick Quach (NASA) **Method to check presence of bug** Not applicable (not a bug). **Expected result** The code produced by Copilot complies with all rules in MISRA C 2012. Any deviations from any rules or directives are documented and justified. **Solution implemented** Modify `copilot-c99` to add keyword `static` to guards and generator functions. That requires using language-c99-simple >= 0.3. Modify `copilot-c99` backend to explicitly cast constants to `size_t` in manipulations of the ring buffers. Add a tool to the CI process that checks for compliance with the standard. **Further notes** The solution includes a new test in the CI setup that uses cppcheck to check that a Copilot-generated C file complies with MISRA C. The test is being executed by the CI setup (see: https://app.travis-ci.com/github/Copilot-Language/copilot/jobs/615908458#L1976-L1978). Furthermore, Parasoft has been used to manually check the same example for compliance. Parasoft's tool reports a violation of one advisory only: Directive 4.6. Complying with that recommendation would require using specific types that indicate the size and signedness instead of float and double. Although we could call those float32_t and float64_t, there is in principle no guarantee that those will be the sizes in all architectures, making such names potentially misleading. Since this is a recommendation, we decide to accept non-compliance with this directive. This change does not modify the README, contrary to the solution originally proposed. This is intentional: there is no suitable place to indicate information about compliance, or lack thereof, with MISRA C. We decide to defer this change, and suggest extending the README to show the features of Copilot more prominently. That will create the space to talk about MISRA compliance and also list the advisory we do not currently comply with.
2 parents 5ee005e + e0dd7f0 commit ca738f7

File tree

5 files changed

+41
-14
lines changed

5 files changed

+41
-14
lines changed

.travis.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# dist should be at least focal because we need cppcheck >= 1.88.
2+
dist: focal
3+
14
# NB: don't set `language: haskell` here
25

36
# The following enables several GHC versions to be tested; often it's enough to
@@ -16,9 +19,9 @@ before_install:
1619
- travis_retry sudo apt-get update
1720
- travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex
1821

19-
# We install z3 only for the tests, since it is not needed for normal
20-
# compilation.
21-
- if [ "${GHCVER}" == "8.10.4" ]; then travis_retry sudo apt-get install --yes z3; fi
22+
# We install z3 and cppcheck only for the tests, since they are not needed
23+
# for normal compilation.
24+
- if [ "${GHCVER}" == "8.10.4" ]; then travis_retry sudo apt-get install --yes z3 cppcheck; fi
2225

2326
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
2427
- cabal --version
@@ -27,7 +30,10 @@ before_install:
2730
- git submodule update --remote
2831

2932
script:
30-
- travis_wait 30 cabal v2-install --lib copilot
33+
# We explicitly install all libraries so that they are exposed and we can use
34+
# them for tests (e.g., with runhaskell). There is no harm in doing this
35+
# instead of installing just copilot.
36+
- travis_wait 30 cabal v2-install --lib copilot copilot-core copilot-c99 copilot-language copilot-libraries copilot-theorem copilot-interpreter copilot-prettyprinter
3137

3238
# Run tests only on GHC 8.10.4
3339
#
@@ -37,3 +43,8 @@ script:
3743
# conditional installation, and keep GHC version numbers in both places in
3844
# sync.
3945
- if [ "${GHCVER}" == "8.10.4" ]; then cabal v2-test -j1 copilot-core copilot-language copilot-interpreter copilot-c99 copilot-theorem copilot-libraries; fi
46+
47+
# Check that the code produced by Copilot complies with MISRA C 2012. We
48+
# explicitly make cppcheck produce a non-zero exit code on non-compliance
49+
# with the standard to make the CI build fail.
50+
- 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

copilot-c99/CHANGELOG

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
2023-12-19
1+
2024-01-06
22
* Change return type of main generated for tests. (#468)
33
* Print constants in tests using portable suffixes. (#471).
44
* Pass output arrays as arguments to trigger argument functions. (#431)
5+
* Compliance with MISRA C 2023 / MISRA C 2012. (#472)
56

67
2023-11-07
78
* Version bump (3.17). (#466)

copilot-c99/copilot-c99.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ library
4747

4848
, copilot-core >= 3.17 && < 3.18
4949
, language-c99 >= 0.2.0 && < 0.3
50-
, language-c99-simple >= 0.2.2 && < 0.3
50+
, language-c99-simple >= 0.3 && < 0.4
5151

5252
exposed-modules : Copilot.Compile.C99
5353

copilot-c99/src/Copilot/Compile/C99/CodeGen.hs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,18 @@ mkIndexDecln sId = C.VarDecln (Just C.Static) cTy name initVal
100100

101101
-- | Define an accessor functions for the ring buffer associated with a stream.
102102
mkAccessDecln :: Id -> Type a -> [a] -> C.FunDef
103-
mkAccessDecln sId ty xs = C.FunDef cTy name params [] [C.Return (Just expr)]
103+
mkAccessDecln sId ty xs =
104+
C.FunDef static cTy name params [] [C.Return (Just expr)]
104105
where
106+
static = Just C.Static
105107
cTy = C.decay $ transType ty
106108
name = streamAccessorName sId
107-
buffLength = C.LitInt $ fromIntegral $ length xs
109+
110+
-- We cast the buffer length to a size_t to make sure that there are no
111+
-- implicit conversions. This is a requirement for compliance with MISRA C
112+
-- (Rule 10.4).
113+
buffLength = C.Cast sizeT $ C.LitInt $ fromIntegral $ length xs
114+
sizeT = C.TypeName $ C.TypeSpec $ C.TypedefName "size_t"
108115
params = [C.Param (C.TypeSpec $ C.TypedefName "size_t") "x"]
109116
index = (C.Ident (indexName sId) C..+ C.Ident "x") C..% buffLength
110117
expr = C.Index (C.Ident (streamName sId)) index
@@ -113,16 +120,19 @@ mkAccessDecln sId ty xs = C.FunDef cTy name params [] [C.Return (Just expr)]
113120

114121
-- | Write a generator function for a stream.
115122
mkGenFun :: String -> Expr a -> Type a -> C.FunDef
116-
mkGenFun name expr ty = C.FunDef cTy name [] cVars [C.Return $ Just cExpr]
123+
mkGenFun name expr ty =
124+
C.FunDef static cTy name [] cVars [C.Return $ Just cExpr]
117125
where
126+
static = Just C.Static
118127
cTy = C.decay $ transType ty
119128
(cExpr, cVars) = runState (transExpr expr) mempty
120129

121130
-- | Write a generator function for a stream that returns an array.
122131
mkGenFunArray :: String -> String -> Expr a -> Type a -> C.FunDef
123132
mkGenFunArray name nameArg expr ty@(Array _) =
124-
C.FunDef funType name [ outputParam ] varDecls stmts
133+
C.FunDef static funType name [ outputParam ] varDecls stmts
125134
where
135+
static = Just C.Static
126136
funType = C.TypeSpec C.Void
127137

128138
-- The output value is an array
@@ -145,7 +155,7 @@ mkGenFunArray _name _nameArg _expr _ty =
145155
-- | Define the step function that updates all streams.
146156
mkStep :: CSettings -> [Stream] -> [Trigger] -> [External] -> C.FunDef
147157
mkStep cSettings streams triggers exts =
148-
C.FunDef void (cSettingsStepFunctionName cSettings) [] declns stmts
158+
C.FunDef Nothing void (cSettingsStepFunctionName cSettings) [] declns stmts
149159
where
150160
void = C.TypeSpec C.Void
151161

@@ -194,8 +204,12 @@ mkStep cSettings streams triggers exts =
194204

195205
indexUpdate = C.Expr $ indexVar C..= (incIndex C..% buffLength)
196206
where
197-
buffLength = C.LitInt $ fromIntegral $ length buff
198-
incIndex = indexVar C..+ C.LitInt 1
207+
-- We cast the buffer length and the literal one to a size_t to
208+
-- make sure that there are no implicit conversions. This is a
209+
-- requirement for compliance with MISRA C (Rule 10.4).
210+
buffLength = C.Cast sizeT $ C.LitInt $ fromIntegral $ length buff
211+
incIndex = indexVar C..+ C.Cast sizeT (C.LitInt 1)
212+
sizeT = C.TypeName $ C.TypeSpec $ C.TypedefName "size_t"
199213

200214
tmpVar = streamName sId ++ "_tmp"
201215
buffVar = C.Ident $ streamName sId

copilot/CHANGELOG

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
2023-12-27
1+
2024-01-06
22
* Enable tests for copilot-theorem in CI script. (#474)
33
* Enable tests for copilot-libraries in CI script. (#475)
44
* Replace uses of forall with forAll. (#470)
5+
* Update CI job to check for MISRA compliance with cppcheck. (#472)
56

67
2023-11-07
78
* Version bump (3.17). (#466)

0 commit comments

Comments
 (0)