Skip to content

Commit f8f7378

Browse files
authored
Merge pull request #209 from goblint/static-assert-check
Check `_Static_assert`
2 parents bf39f60 + 6914845 commit f8f7378

11 files changed

Lines changed: 135 additions & 27 deletions

src/frontc/cabs.ml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ type typeSpecifier = (* Merge all specifiers into one type *)
8383
a forward declaration or simple reference to the type); they
8484
also have a list of __attribute__s that appeared between the
8585
keyword and the type name (definitions only) *)
86-
| Tstruct of string * field_group list option * attribute list
87-
| Tunion of string * field_group list option * attribute list
86+
| Tstruct of string * struct_decl list option * attribute list
87+
| Tunion of string * struct_decl list option * attribute list
8888
| Tenum of string * enum_item list option * attribute list
8989
| TtypeofE of expression (* GCC __typeof__ *)
9090
| TtypeofT of specifier * decl_type (* GCC __typeof__ *)
@@ -150,6 +150,10 @@ and name_group = specifier * name list
150150
(* The optional expression is the bitfield *)
151151
and field_group = specifier * (name * expression option) list
152152

153+
and struct_decl =
154+
| FIELD_GROUP of field_group
155+
| FIELD_STATIC_ASSERT of expression * string option * cabsloc
156+
153157
(* like name_group, except the declared variables are allowed to have initializers *)
154158
(* e.g.: int x=1, y=2; *)
155159
and init_name_group = specifier * init_name list
@@ -187,7 +191,7 @@ and definition =
187191
| TRANSFORMER of definition * definition list * cabsloc
188192
(* expression transformer: source and destination *)
189193
| EXPRTRANSFORMER of expression * expression * cabsloc
190-
| STATIC_ASSERT of expression * string * cabsloc
194+
| STATIC_ASSERT of expression * string option * cabsloc
191195

192196

193197
(* the string is a file name, and then the list of toplevel forms *)

src/frontc/cabs2cil.ml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,7 @@ let rec doSpecList (suggestedAnonName: string) (* This string will be part of
26672667
if n = "" then E.s (error "Missing struct tag on incomplete struct");
26682668
findCompType "struct" n []
26692669
| [A.Tstruct (n, Some nglist, extraAttrs)] -> (* A definition of a struct *)
2670+
let nglist = doStructDecls nglist in
26702671
let (specs, names) = List.split nglist in
26712672
let n' =
26722673
if n <> "" then n else anonStructName "struct" suggestedAnonName specs in
@@ -2678,6 +2679,7 @@ let rec doSpecList (suggestedAnonName: string) (* This string will be part of
26782679
if n = "" then E.s (error "Missing union tag on incomplete union");
26792680
findCompType "union" n []
26802681
| [A.Tunion (n, Some nglist, extraAttrs)] -> (* A definition of a union *)
2682+
let nglist = doStructDecls nglist in
26812683
let (specs, names) = List.split nglist in
26822684
let n' =
26832685
if n <> "" then n else anonStructName "union" suggestedAnonName specs in
@@ -2827,6 +2829,23 @@ let rec doSpecList (suggestedAnonName: string) (* This string will be part of
28272829
in
28282830
bt,!storage,!isinline,List.rev (!attrs @ (convertCVtoAttr !cvattrs))
28292831

2832+
2833+
and doStructDecls (struct_decls: struct_decl list): field_group list =
2834+
List.filter_map (function
2835+
| FIELD_GROUP fg -> Some fg
2836+
| FIELD_STATIC_ASSERT (e, str, loc) ->
2837+
let loc = convLoc loc in
2838+
let d_message () = function
2839+
| None -> nil
2840+
| Some str -> dprintf ": %s" str
2841+
in
2842+
begin match isIntegerConstant e with
2843+
| Some 0 -> E.s (error "Static assert failed at %a%a" d_loc loc d_message str)
2844+
| Some _ -> None
2845+
| None -> E.s (error "Static assert with a non-constant at %a%a" d_loc loc d_message str)
2846+
end
2847+
) struct_decls
2848+
28302849
(* given some cv attributes, convert them into named attributes for
28312850
uniform processing *)
28322851
and convertCVtoAttr (src: A.cvspec list) : A.attribute list =
@@ -6624,7 +6643,19 @@ and doDecl (isglobal: bool) (isstmt: bool) : A.definition -> chunk = function
66246643
E.s (bug "doDecl returns non-empty statement for global"))
66256644
dl;
66266645
empty
6627-
| STATIC_ASSERT _ -> empty
6646+
| STATIC_ASSERT (e, str, loc) ->
6647+
if isglobal || isstmt then
6648+
currentLoc := convLoc loc;
6649+
currentExpLoc := convLoc loc;
6650+
let d_message () = function
6651+
| None -> nil
6652+
| Some str -> dprintf ": %s" str
6653+
in
6654+
begin match isIntegerConstant e with
6655+
| Some 0 -> E.s (error "Static assert failed at %a%a" d_loc !currentLoc d_message str)
6656+
| Some _ -> empty
6657+
| None -> E.s (error "Static assert with a non-constant at %a%a" d_loc !currentLoc d_message str)
6658+
end
66286659
| _ -> E.s (error "unexpected form of declaration")
66296660

66306661
and doTypedef ((specs, nl): A.name_group) =

src/frontc/cabsvisit.ml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,22 @@ and childrenTypeSpecifier vis ts =
189189
let nel' = mapNoCopy doOneField nel in
190190
if s' != s || nel' != nel then (s', nel') else input
191191
in
192+
let childrenStructDecl input =
193+
match input with
194+
| FIELD_GROUP fg ->
195+
let fg' = childrenFieldGroup fg in
196+
if fg' != fg then FIELD_GROUP fg' else input
197+
| FIELD_STATIC_ASSERT (e, str, l) ->
198+
let e' = visitCabsExpression vis e in
199+
if e' != e then FIELD_STATIC_ASSERT (e', str, l) else input
200+
in
192201
match ts with
193202
Tstruct (n, Some fg, extraAttrs) ->
194203
(*(trace "sm" (dprintf "visiting struct %s\n" n));*)
195-
let fg' = mapNoCopy childrenFieldGroup fg in
204+
let fg' = mapNoCopy childrenStructDecl fg in
196205
if fg' != fg then Tstruct( n, Some fg', extraAttrs) else ts
197206
| Tunion (n, Some fg, extraAttrs) ->
198-
let fg' = mapNoCopy childrenFieldGroup fg in
207+
let fg' = mapNoCopy childrenStructDecl fg in
199208
if fg' != fg then Tunion( n, Some fg', extraAttrs) else ts
200209
| Tenum (n, Some ei, extraAttrs) ->
201210
let doOneEnumItem ((s, attrs, e, loc) as ei) =
@@ -321,7 +330,9 @@ and childrenDefinition vis d =
321330
let dl' = mapNoCopyList (visitCabsDefinition vis) dl in
322331
if dl' != dl then LINKAGE (n, l, dl') else d
323332

324-
| STATIC_ASSERT _ -> d
333+
| STATIC_ASSERT (e, str, l) ->
334+
let e' = visitCabsExpression vis e in
335+
if e' != e then STATIC_ASSERT (e', str, l) else d
325336
| TRANSFORMER _ -> d
326337
| EXPRTRANSFORMER _ -> d
327338

src/frontc/cparser.mly

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ let transformOffsetOf (speclist, dtype) member =
370370

371371
%type <spec_elem list * cabsloc> decl_spec_list
372372
%type <typeSpecifier * cabsloc> type_spec
373-
%type <Cabs.field_group list> struct_decl_list
373+
%type <Cabs.struct_decl list> struct_decl_list
374374

375375

376376
%type <Cabs.name> old_proto_decl
@@ -999,18 +999,18 @@ declaration: /* ISO 6.7.*/
999999
{ doDeclaration (joinLoc (snd $1) $3) (fst $1) $2 }
10001000
| decl_spec_list_no_attr_only SEMICOLON
10011001
{ doDeclaration (joinLoc (snd $1) $2) (fst $1) [] }
1002-
| static_assert_declaration { let (e, m, loc) = $1 in STATIC_ASSERT (e, m, loc) }
1002+
| static_assert_declaration SEMICOLON { let (e, m, loc) = $1 in STATIC_ASSERT (e, m, loc) }
10031003
;
10041004

10051005
static_assert_declaration:
10061006

10071007
| STATIC_ASSERT LPAREN expression RPAREN /* C23 */
10081008
{
1009-
(fst $3, "", $1)
1009+
(fst $3, None, $1)
10101010
}
10111011
| STATIC_ASSERT LPAREN expression COMMA const_raw_string RPAREN
10121012
{
1013-
(fst $3, fst $5, $1)
1013+
(fst $3, Some (fst $5), $1)
10141014
}
10151015
;
10161016

@@ -1149,26 +1149,23 @@ struct_decl_list: /* (* ISO 6.7.2. Except that we allow empty structs. We
11491149
*/
11501150
/* empty */ { [] }
11511151
| decl_spec_list SEMICOLON struct_decl_list
1152-
{ (fst $1,
1152+
{ FIELD_GROUP (fst $1,
11531153
[(missingFieldDecl, None)]) :: $3 }
11541154
/*(* GCC allows extra semicolons *)*/
11551155
| SEMICOLON struct_decl_list
11561156
{ $2 }
11571157
| decl_spec_list field_decl_list SEMICOLON struct_decl_list
1158-
{ (fst $1, $2)
1158+
{ FIELD_GROUP (fst $1, $2)
11591159
:: $4 }
11601160
/*(* MSVC allows pragmas in strange places *)*/
11611161
| pragma struct_decl_list { $2 }
11621162

11631163
| error SEMICOLON struct_decl_list
11641164
{ $3 }
11651165
/*(* C11 allows static_assert-declaration *)*/
1166-
| static_assert_declaration {
1167-
[]
1168-
}
1169-
11701166
| static_assert_declaration SEMICOLON struct_decl_list {
1171-
$3
1167+
let (e, m, loc) = $1 in
1168+
FIELD_STATIC_ASSERT (e, m, loc) :: $3
11721169
}
11731170

11741171
;

src/frontc/cprint.ml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,26 @@ and print_decl (n: string) = function
244244
comprint ")"
245245

246246

247-
and print_fields (flds : field_group list) =
247+
and print_fields (flds : struct_decl list) =
248248
if flds = [] then print " { } "
249249
else begin
250250
print " {";
251251
indent ();
252252
List.iter
253-
(fun fld -> print_field_group fld; print ";"; new_line ())
253+
(function
254+
| FIELD_GROUP fld -> print_field_group fld; print ";"; new_line ()
255+
| FIELD_STATIC_ASSERT (e, str, loc) ->
256+
print "_Static_assert(";
257+
print_expression e;
258+
begin match str with
259+
| Some str ->
260+
print ",";
261+
print_string str;
262+
| None -> ()
263+
end;
264+
print ");";
265+
new_line ()
266+
)
254267
flds;
255268
unindent ();
256269
print "} "
@@ -909,8 +922,12 @@ and print_def def =
909922
setLoc(loc);
910923
print "_Static_assert(";
911924
print_expression e;
912-
print ",";
913-
print_string str;
925+
begin match str with
926+
| Some str ->
927+
print ",";
928+
print_string str;
929+
| None -> ()
930+
end;
914931
print ");";
915932

916933
(* sm: print a comment if the printComments flag is set *)

src/frontc/patch.ml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,12 +558,18 @@ begin
558558
)
559559
end
560560

561-
and unifyField (pat : field_group) (tgt : field_group) : binding list =
561+
and unifyField (pat : struct_decl) (tgt : struct_decl) : binding list =
562562
begin
563-
match pat,tgt with (spec1, list1), (spec2, list2) -> (
563+
match pat, tgt with
564+
| FIELD_GROUP (spec1, list1), FIELD_GROUP (spec2, list2) ->
564565
(unifySpecifiers spec1 spec2) @
565566
(unifyList list1 list2 unifyNameExprOpt)
566-
)
567+
| FIELD_STATIC_ASSERT (exp1, str1, l1), FIELD_STATIC_ASSERT (exp2, str2, l2) ->
568+
(unifyExpr exp1 exp2) @
569+
(unifyStringOpt str1 str2)
570+
| _, _ ->
571+
if verbose then (trace "patchDebug" (dprintf "mismatching struct_decl-s\n"));
572+
raise NoMatch
567573
end
568574

569575
and unifyNameExprOpt (pat : name * expression option)
@@ -700,6 +706,15 @@ begin
700706
)
701707
end
702708

709+
and unifyStringOpt (pat : string option)
710+
(tgt : string option) : binding list =
711+
begin
712+
match pat,tgt with
713+
| None, None -> []
714+
| Some str1, Some str2 -> unifyString str1 str2
715+
| _,_ -> raise NoMatch
716+
end
717+
703718
and unifyExpr (pat : expression) (tgt : expression) : binding list =
704719
begin
705720
(* if they're equal, that's good enough *)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "testharness.h"
2+
3+
_Static_assert (2 > 18, "blubb");
4+
5+
int main() {
6+
SUCCESS;
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "testharness.h"
2+
3+
int main() {
4+
_Static_assert (2 > 18, "blubb");
5+
SUCCESS;
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "testharness.h"
2+
3+
struct S {
4+
int x;
5+
_Static_assert (2 > 18, "blubb");
6+
};
7+
8+
int main() {
9+
SUCCESS;
10+
}

test/small1/c11-static-assert.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
#include "testharness.h"
22
#include <stdnoreturn.h>
33

4-
_Static_assert (2 <= 18, "blubb");
4+
_Static_assert (2 <= 18);
5+
_Static_assert (2 <= 18, "blubb");
56

7+
struct S {
8+
int x;
9+
_Static_assert (2 <= 18);
10+
_Static_assert (2 <= 18, "blubb");
11+
};
612

713
int main() {
8-
_Static_assert (2 <= 18, "blubb");
14+
_Static_assert (2 <= 18);
15+
_Static_assert (2 <= 18, "blubb");
916
SUCCESS;
1017
}

0 commit comments

Comments
 (0)