Commit 175ee43
committed
Merge branch 'develop-error-message-empty-array-struct'. Close #695.
**Description**
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.
**Type**
* Bug: Copilot crashes or generates incorrect code.
**Additional context**
None.
**Requester**
* Ryan Scott (Galois)
**Method to check presence of bug**
The following Copilot specification contains an empty array:
```hs
-- EmptyArray.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Language.Copilot
import Copilot.Compile.C99 (compile)
arrs :: Stream (Array 0 Bool)
arrs = constant (array [])
spec :: Spec
spec = trigger "trig" true [arg arrs]
main :: IO ()
main = do
spec' <- reify spec
compile "empty_array" spec'
```
And the following Copilot specification contains an empty struct:
```hs
-- EmptyStruct.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import GHC.Generics (Generic)
import Language.Copilot
import Copilot.Compile.C99 (compile)
data Empty = Empty deriving Generic
instance Struct Empty where
typeName = typeNameDefault
toValues = toValuesDefault
updateField = updateFieldDefault
instance Typed Empty where
typeOf = typeOfDefault
structs :: Stream Empty
structs = constant Empty
spec :: Spec
spec = trigger "trig" true [arg structs]
main :: IO ()
main = do
spec' <- reify spec
compile "empty_struct" spec'
```
Compiling either program will cause `copilot-c99` to crash:
```
$ runghc EmptyArray.hs
EmptyArray.hs: NonEmpty.fromList: empty list
CallStack (from HasCallStack):
error, called at libraries/base/Data/List/NonEmpty.hs:200:15 in base:Data.List.NonEmpty
fromList, called at src/Copilot/Compile/C99/Expr.hs:381:3 in copilot-c99-4.6-inplace:Copilot.Compile.C99.Expr
$ runghc EmptyStruct.hs
EmptyStruct.hs: NonEmpty.fromList: empty list
CallStack (from HasCallStack):
error, called at libraries/base/Data/List/NonEmpty.hs:200:15 in base:Data.List.NonEmpty
fromList, called at src/Copilot/Compile/C99/Expr.hs:375:19 in copilot-c99-4.6-inplace:Copilot.Compile.C99.Expr
```
Note that not all Copilot specifications which contain empty arrays or
structs will cause Copilot to crash outright. For instance, the
following program contains an empty array, but only in the type of an
extern:
```hs
-- EmptyArrayExtern.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Language.Copilot
import Copilot.Compile.C99 (compile)
arrs :: Stream (Array 0 Bool)
arrs = extern "arrs" Nothing
spec :: Spec
spec = trigger "trig" true [arg arrs]
main :: IO ()
main = do
spec' <- reify spec
compile "empty_array_extern" spec'
```
`copilot-c99` will compile this to C code, but using a C compiler such as `gcc` or `clang` will warn you that the resulting C code falls outside of C99 if you use the `-Wpedantic` flag:
```
$ clang -c empty_array_extern.c -o empty_array_extern.o -Wpedantic
In file included from empty_array_extern.c:8:
./empty_array_extern.h:1:18: warning: zero size arrays are an extension [-Wzero-length-array]
1 | extern bool arrs[(0)];
| ^~~
./empty_array_extern.h:2:26: warning: zero size arrays are an extension [-Wzero-length-array]
2 | void trig(bool trig_arg0[(0)]);
| ^~~
empty_array_extern.c:10:22: warning: zero size arrays are an extension [-Wzero-length-array]
10 | static bool arrs_cpy[(0)];
| ^~~
empty_array_extern.c:16:49: warning: zero size arrays are an extension [-Wzero-length-array]
16 | static void trig_0_arg0(bool trig_0_arg0_output[(0)]) {
| ^~~
empty_array_extern.c:21:25: warning: zero size arrays are an extension [-Wzero-length-array]
21 | bool trig_0_arg_temp0[(0)];
| ^~~
5 warnings generated.
```
**Expected result**
A clear error message is raised either by the Copilot compiler or GCC
indicating that the code produced involving empty arrays or structs is
not valid.
The following Dockerfile checks that compiling an empty struct or array
raises fails during compilation, after which it prints the message
Success:
```
--- Dockerfile-verify-695
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
SHELL ["/bin/bash", "-c"]
RUN apt-get update
RUN apt-get install --yes software-properties-common
RUN add-apt-repository ppa:hvr/ghc
RUN apt-get update
RUN apt-get install --yes ghc-8.6.5 cabal-install-2.4
RUN apt-get install --yes libz-dev git alex happy
ENV PATH=/opt/ghc/8.6.5/bin:/opt/cabal/2.4/bin:$PWD/.cabal-sandbox/bin:$PATH
RUN cabal update
RUN cabal v1-sandbox init
ADD EmptyArray.hs /tmp/
ADD EmptyStruct.hs /tmp/
CMD git clone $REPO && cd $NAME && git checkout $COMMIT && cd .. \
&& cabal v1-install \
$NAME/copilot \
$NAME/copilot-c99 \
$NAME/copilot-core \
$NAME/copilot-interpreter \
$NAME/copilot-language \
$NAME/copilot-libraries \
$NAME/copilot-prettyprinter \
$NAME/copilot-theorem \
&& ! cabal v1-exec -- runhaskell /tmp/EmptyArray.hs \
&& ! cabal v1-exec -- runhaskell /tmp/EmptyStruct.hs \
&& echo "Success"
--- EmptyArray.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Copilot.Compile.C99 (compile)
import Language.Copilot
main :: IO ()
main = do
spec' <- reify spec
compile "empty_array" spec'
spec :: Spec
spec = trigger "trig" true [arg arrs]
arrs :: Stream (Array 0 Bool)
arrs = constant (array [])
--- EmptyStruct.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import GHC.Generics (Generic)
import Copilot.Compile.C99 (compile)
import Language.Copilot
main :: IO ()
main = do
spec' <- reify spec
compile "empty_struct" spec'
spec :: Spec
spec = trigger "trig" true [arg structs]
structs :: Stream Empty
structs = constant Empty
-- * Empty struct type
data Empty = Empty
deriving Generic
instance Struct Empty where
typeName = typeNameDefault
toValues = toValuesDefault
updateField = updateFieldDefault
instance Typed Empty where
typeOf = typeOfDefault
```
Command (substitute variables based on new path after merge):
```
$ docker run -e "REPO=https://github.com/Copilot-Language/copilot" -e "NAME=copilot" -e "COMMIT=<HASH>" -it copilot-verify-695
```
**Solution implemented**
Modify `copilot-c99`'s `constStruct` and `constArray` to raise an error
if a struct or array passed as argument is empty.
Modify `copilot-c99`'s `transType` to raise an error if a struct or
array passed as argument is empty.
Add test cases to `copilot-c99` to ensure that Copilot specifications
with empty arrays or structs are rejected by the C99 backend.
**Further notes**
None.5 files changed
Lines changed: 107 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
1 | 4 | | |
2 | 5 | | |
3 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
9 | 12 | | |
10 | 13 | | |
11 | 14 | | |
| |||
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
371 | 371 | | |
372 | 372 | | |
373 | 373 | | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
374 | 377 | | |
375 | | - | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
376 | 381 | | |
377 | 382 | | |
378 | 383 | | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
379 | 387 | | |
380 | | - | |
381 | | - | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
382 | 391 | | |
383 | 392 | | |
384 | 393 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
31 | | - | |
| 33 | + | |
| 34 | + | |
32 | 35 | | |
33 | 36 | | |
34 | | - | |
| 37 | + | |
| 38 | + | |
35 | 39 | | |
36 | 40 | | |
37 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
13 | 14 | | |
14 | | - | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| |||
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| 28 | + | |
27 | 29 | | |
| 30 | + | |
28 | 31 | | |
29 | 32 | | |
30 | 33 | | |
| |||
50 | 53 | | |
51 | 54 | | |
52 | 55 | | |
| 56 | + | |
53 | 57 | | |
54 | 58 | | |
55 | 59 | | |
| |||
137 | 141 | | |
138 | 142 | | |
139 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
140 | 203 | | |
141 | 204 | | |
142 | 205 | | |
| |||
0 commit comments