Skip to content

Commit 568dc23

Browse files
Merge pull request #48 from goblint/c11-generic
Add C11 generic support
2 parents f3a075d + a0a1a94 commit 568dc23

34 files changed

+452
-67
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Add ends to locations, allowing for ranges.
33
* Add additional expression locations to statements, etc.
44
* Drop all support for MSVC
5+
* Add C11 `_Generic` support.
56

67
## Older versions
78

Makefile.in

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ $(OBJDIR)/machdep.ml : src/machdep-ml.c configure.ac Makefile.in
190190
@echo " alignof_fun: int; (* Alignment of function *)" >> $@
191191
@echo " alignof_aligned: int; (* Alignment of anything with the \"aligned\" attribute *)" >> $@
192192
@echo " char_is_unsigned: bool; (* Whether \"char\" is unsigned *)">> $@
193-
@echo " const_string_literals: bool; (* Whether string literals have const chars *)">> $@
194193
@echo " little_endian: bool; (* whether the machine is little endian *)">>$@
195194
@echo " __thread_is_keyword: bool; (* whether __thread is a keyword *)">>$@
196195
@echo " __builtin_va_list: bool; (* whether __builtin_va_list is builtin (gccism) *)">>$@
@@ -286,7 +285,7 @@ clean: $(CILLYDIR)/Makefile
286285

287286
.PHONY: test
288287
test:
289-
cd test; CC=@CC@ ./testcil -r --regrtest || { cat cil.log; exit 1; }
288+
cd test; CC=@CC@ ./testcil -r --regrtest || { echo "Check test/cil.log for raw output"; exit 1; }
290289

291290
########################################################################
292291

doc/cil.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ \subsection{Specifying a machine model}
18511851
short=2,2 int=4,4 long=8,8 long_long=8,8 pointer=8,8 enum=4,4
18521852
float=4,4 double=8,8 long_double=16,16 void=1 bool=1,1 fun=1,1
18531853
alignof_string=1 max_alignment=16 size_t=unsigned_long
1854-
wchar_t=int char_signed=true const_string_literals=true
1854+
wchar_t=int char_signed=true
18551855
big_endian=false __thread_is_keyword=true
18561856
__builtin_va_list=true underscore_name=true
18571857
\end{verbatim}

src/check.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ let typeSigIgnoreConst (t : typ) : typsig =
208208
let attrFilter (attr : attribute) : bool =
209209
match attr with
210210
| Attr ("const", []) -> false
211+
| Attr ("pconst", []) -> false
211212
| _ -> true
212213
in
213214
typeSigWithAttrs (List.filter attrFilter) t

src/cil.ml

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,8 +1276,8 @@ let charType = TInt(IChar, [])
12761276
let boolType = TInt(IBool, [])
12771277

12781278
let charPtrType = TPtr(charType,[])
1279-
let charConstPtrType = TPtr(TInt(IChar, [Attr("const", [])]),[])
1280-
let stringLiteralType = ref charPtrType
1279+
let charConstPtrType = TPtr(TInt(IChar, [Attr("const", []); Attr("pconst", [])]),[])
1280+
let stringLiteralType = charPtrType
12811281

12821282
let voidPtrType = TPtr(voidType, [])
12831283
let intPtrType = TPtr(intType, [])
@@ -1523,6 +1523,18 @@ let rec typeAttrs = function
15231523
| TFun (_, _, _, a) -> a
15241524
| TBuiltin_va_list a -> a
15251525

1526+
(** [typeAttrs], which doesn't add inner attributes. *)
1527+
let typeAttrsOuter = function
1528+
| TVoid a -> a
1529+
| TInt (_, a) -> a
1530+
| TFloat (_, a) -> a
1531+
| TNamed (_, a) -> a
1532+
| TPtr (_, a) -> a
1533+
| TArray (_, _, a) -> a
1534+
| TComp (_, a) -> a
1535+
| TEnum (_, a) -> a
1536+
| TFun (_, _, _, a) -> a
1537+
| TBuiltin_va_list a -> a
15261538

15271539
let setTypeAttrs t a =
15281540
match t with
@@ -1574,6 +1586,19 @@ let typeRemoveAttributes (anl: string list) t =
15741586
| TNamed (t, a) -> TNamed (t, drop a)
15751587
| TBuiltin_va_list a -> TBuiltin_va_list (drop a)
15761588

1589+
(** Partition attributes into type qualifiers and non type qualifiers. *)
1590+
let partitionQualifierAttributes al =
1591+
List.partition (function
1592+
| Attr (("const" | "volatile" | "restrict"), []) -> true
1593+
| _ -> false
1594+
) al
1595+
1596+
(** Remove top-level type qualifiers from type. *)
1597+
let removeOuterQualifierAttributes t =
1598+
let a = typeAttrsOuter t in
1599+
let (_, a') = partitionQualifierAttributes a in
1600+
setTypeAttrs t a'
1601+
15771602
let unrollType (t: typ) : typ =
15781603
let rec withAttrs (al: attributes) (t: typ) : typ =
15791604
match t with
@@ -1910,7 +1935,7 @@ let rec typeOf (e: exp) : typ =
19101935
(* The type of a string is a pointer to characters ! The only case when
19111936
* you would want it to be an array is as an argument to sizeof, but we
19121937
* have SizeOfStr for that *)
1913-
| Const(CStr s) -> !stringLiteralType
1938+
| Const(CStr s) -> stringLiteralType
19141939

19151940
| Const(CWStr s) -> TPtr(!wcharType,[])
19161941

@@ -2149,11 +2174,7 @@ let rec getInteger (e:exp) : cilint option =
21492174
let mkInt ik n = Some (fst (truncateCilint ik n)) in
21502175
match unrollType t, getInteger e with
21512176
| TInt (ik, _), Some n -> mkInt ik n
2152-
| TPtr _, Some n -> begin
2153-
match !upointType with
2154-
TInt (ik, _) -> mkInt ik n
2155-
| _ -> raise (Failure "pointer size unknown")
2156-
end
2177+
(* "integer constant expressions" may not cast to ptr *)
21572178
| TEnum (ei, _), Some n -> mkInt ei.ekind n
21582179
| TFloat _, v -> v
21592180
| _, _ -> None
@@ -2671,9 +2692,12 @@ let isArrayType t =
26712692

26722693
(** 6.3.2.3 subsection 3
26732694
* An integer constant expr with value 0, or such an expr cast to void *, is called a null pointer constant. *)
2674-
let isNullPtrConstant = function
2675-
| CastE(TPtr(TVoid _,_), e) -> isZero @@ constFold true e
2676-
| e -> isZero @@ constFold true e
2695+
let isNullPtrConstant e =
2696+
let rec isNullPtrConstant = function
2697+
| CastE (TPtr (TVoid [], []), e) -> isNullPtrConstant e (* no qualifiers allowed on void or ptr *)
2698+
| e -> isZero e
2699+
in
2700+
isNullPtrConstant (constFold true e)
26772701

26782702
let rec isConstant = function
26792703
| Const _ -> true
@@ -2798,7 +2822,7 @@ let initGccBuiltins () : unit =
27982822
let ulongLongType = TInt(IULongLong, []) in
27992823
let floatType = TFloat(FFloat, []) in
28002824
let longDoubleType = TFloat (FLongDouble, []) in
2801-
let voidConstPtrType = TPtr(TVoid [Attr ("const", [])], []) in
2825+
let voidConstPtrType = TPtr(TVoid [Attr ("const", []); Attr ("pconst", [])], []) in
28022826
let sizeType = !typeOfSizeOf in
28032827
let v4sfType = TFloat (FFloat,[Attr("__vector_size__", [AInt 16])]) in
28042828

@@ -4176,7 +4200,7 @@ class defaultCilPrinterClass : cilPrinter = object (self)
41764200

41774201
| TArray (elemt, lo, a) ->
41784202
(* ignore the const attribute for arrays *)
4179-
let a' = dropAttributes [ "const" ] a in
4203+
let a' = dropAttributes [ "pconst" ] a in
41804204
let name' =
41814205
if a' == [] then name else
41824206
if nameOpt == None then printAttributes a' else
@@ -4241,7 +4265,8 @@ class defaultCilPrinterClass : cilPrinter = object (self)
42414265
method pAttr (Attr(an, args): attribute) : doc * bool =
42424266
(* Recognize and take care of some known cases *)
42434267
match an, args with
4244-
"const", [] -> text "const", false
4268+
"const", [] -> nil, false (* don't print const directly, because of split local declarations *)
4269+
| "pconst", [] -> text "const", false (* pconst means print const *)
42454270
(* Put the aconst inside the attribute list *)
42464271
| "complex", [] when !c99Mode -> text "_Complex", false
42474272
| "complex", [] -> text "__complex__", false
@@ -4805,7 +4830,7 @@ let makeVarinfo global name ?init typ =
48054830
{ vname = name;
48064831
vid = newVID ();
48074832
vglob = global;
4808-
vtype = if global then typ else typeRemoveAttributes ["const"] typ;
4833+
vtype = if global then typ else typeRemoveAttributes ["pconst"] typ;
48094834
vdecl = lu;
48104835
vinit = {init=init};
48114836
vinline = false;
@@ -6712,11 +6737,6 @@ let initCIL () =
67126737
Some machine -> M.theMachine := machine
67136738
| None -> M.theMachine := M.gcc
67146739
end;
6715-
(* Pick type for string literals *)
6716-
stringLiteralType := if !M.theMachine.M.const_string_literals then
6717-
charConstPtrType
6718-
else
6719-
charPtrType;
67206740
(* Find the right ikind given the size *)
67216741
let findIkindSz (unsigned: bool) (sz: int) : ikind =
67226742
try

src/cil.mli

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,9 @@ val charType: typ
13321332
(** char * *)
13331333
val charPtrType: typ
13341334

1335+
(** Type of string literals *)
1336+
val stringLiteralType: typ
1337+
13351338
(** wchar_t (depends on architecture) and is set when you call
13361339
* {!Cil.initCIL}. *)
13371340
val wcharKind: ikind ref
@@ -1799,6 +1802,9 @@ val hasAttribute: string -> attributes -> bool
17991802
of the type structure, in case of composite, enumeration and named types *)
18001803
val typeAttrs: typ -> attribute list
18011804

1805+
(** [typeAttrs], which doesn't add inner attributes. *)
1806+
val typeAttrsOuter: typ -> attribute list
1807+
18021808
val setTypeAttrs: typ -> attributes -> typ (* Resets the attributes *)
18031809

18041810

@@ -1811,6 +1817,13 @@ val typeAddAttributes: attribute list -> typ -> typ
18111817
val typeRemoveAttributes: string list -> typ -> typ
18121818

18131819

1820+
(** Partition attributes into type qualifiers and non type qualifiers. *)
1821+
val partitionQualifierAttributes: attribute list -> attribute list * attribute list
1822+
1823+
(** Remove top-level type qualifiers from type. *)
1824+
val removeOuterQualifierAttributes: typ -> typ
1825+
1826+
18141827
(** Convert an expression into an attrparam, if possible. Otherwise raise
18151828
NotAnAttrParam with the offending subexpression *)
18161829
val expToAttrParam: exp -> attrparam

src/frontc/cabs.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ type typeSpecifier = (* Merge all specifiers into one type *)
8383
| Tenum of string * enum_item list option * attribute list
8484
| TtypeofE of expression (* GCC __typeof__ *)
8585
| TtypeofT of specifier * decl_type (* GCC __typeof__ *)
86+
| Tdefault (** "default" in generic associations *)
8687

8788
and storage =
8889
NO_STORAGE | AUTO | STATIC | EXTERN | REGISTER
@@ -280,6 +281,7 @@ and expression =
280281
| MEMBEROFPTR of expression * string
281282
| GNU_BODY of block
282283
| EXPR_PATTERN of string (* pattern variable, and name *)
284+
| GENERIC of expression * (((specifier * decl_type) * expression) list)
283285

284286
and constant =
285287
| CONST_INT of string (* the textual representation *)

src/frontc/cabs2cil.ml

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -603,11 +603,7 @@ let alphaConvertVarAndAddToEnv (addtoenv: bool) (vi: varinfo) : varinfo =
603603
* a struct we must recursively strip the "const" from fields and array
604604
* elements. *)
605605
let rec stripConstLocalType (t: typ) : typ =
606-
let dc a =
607-
if hasAttribute "const" a then
608-
dropAttribute "const" a
609-
else a
610-
in
606+
let dc = dropAttribute "pconst" in
611607
match t with
612608
| TPtr (bt, a) ->
613609
(* We want to be able to detect by pointer equality if the type has
@@ -1570,7 +1566,16 @@ type combineWhat =
15701566
* that are known to be equal *)
15711567
let isomorphicStructs : (string * string, bool) H.t = H.create 15
15721568

1569+
(** Construct the composite type of [oldt] and [t] if they are compatible.
1570+
Raise [Failure] if they are incompatible. *)
15731571
let rec combineTypes (what: combineWhat) (oldt: typ) (t: typ) : typ =
1572+
let (oldq, olda) = partitionQualifierAttributes (typeAttrsOuter oldt) in
1573+
let (q, a) = partitionQualifierAttributes (typeAttrsOuter t) in
1574+
if oldq <> q then
1575+
raise (Failure "different type qualifiers")
1576+
else if q <> [] then
1577+
cabsTypeAddAttributes q (combineTypes what (setTypeAttrs oldt olda) (setTypeAttrs t a))
1578+
else
15741579
match oldt, t with
15751580
| TVoid olda, TVoid a -> TVoid (cabsAddAttributes olda a)
15761581
| TInt (oldik, olda), TInt (ik, a) ->
@@ -1743,7 +1748,7 @@ let rec combineTypes (what: combineWhat) (oldt: typ) (t: typ) : typ =
17431748
combineTypes
17441749
(if what = CombineFundef then
17451750
CombineFunarg else CombineOther)
1746-
ot' at
1751+
(removeOuterQualifierAttributes ot') (removeOuterQualifierAttributes at)
17471752
in
17481753
let a = addAttributes oa aa in
17491754
(n, t, a))
@@ -1883,19 +1888,33 @@ let conditionalConversion (t2: typ) (t3: typ) (e2: exp option) (e3:exp) : typ =
18831888
arithmeticConversion t2 t3
18841889
| TComp (comp2,_), TComp (comp3,_), _
18851890
when comp2.ckey = comp3.ckey -> t2
1886-
| TPtr(_, _), TPtr(TVoid _, _), _ ->
1887-
if isNullPtrConstant e3 then t2 else t3
1888-
| TPtr(TVoid _, _), TPtr(_, _), Some e2' ->
1889-
if isNullPtrConstant e2' then t3 else t2
1891+
| TVoid [], TVoid [], _ -> TVoid [] (* TODO: what about qualifiers? standard says nothing *)
1892+
| TPtr(_, _), _, _ when isNullPtrConstant e3 -> t2
1893+
| _, TPtr(_, _), Some e2' when isNullPtrConstant e2' -> t3
1894+
| TPtr(b2, _), TPtr(TVoid _ as b3, _), _
1895+
| TPtr(TVoid _ as b2, _), TPtr(b3, _), _ ->
1896+
let a2 = typeAttrsOuter b2 in
1897+
let a3 = typeAttrsOuter b3 in
1898+
let (q2, _) = partitionQualifierAttributes a2 in
1899+
let (q3, _) = partitionQualifierAttributes a3 in
1900+
let q = cabsAddAttributes q2 q3 in
1901+
TPtr (TVoid q, [])
18901902
| TPtr _, TPtr _, _ when Util.equals (typeSig t2) (typeSig t3) -> t2
1891-
| TPtr _, TInt _, _ -> t2 (* most likely comparison with int constant 0, if it isn't it would not be valid C *)
1892-
| TInt _, TPtr _, _ -> t3 (* most likely comparison with int constant 0, if it isn't it would not be valid C *)
1903+
| TPtr _, TInt _, _ -> t2 (* not "null pointer constant", not allowed by standard, works in gcc/clang with warning *)
1904+
| TInt _, TPtr _, _ -> t3 (* not "null pointer constant", not allowed by standard, works in gcc/clang with warning *)
18931905

18941906
(* When we compare two pointers of different type, we combine them
18951907
* using the same algorithm when combining multiple declarations of
18961908
* a global *)
1897-
| (TPtr _) as t2', (TPtr _ as t3'), _ -> begin
1898-
try combineTypes CombineOther t2' t3'
1909+
| TPtr (b2, _), TPtr (b3, _), _ -> begin
1910+
try
1911+
let a2 = typeAttrsOuter b2 in
1912+
let a3 = typeAttrsOuter b3 in
1913+
let (q2, a2') = partitionQualifierAttributes a2 in
1914+
let (q3, a3') = partitionQualifierAttributes a3 in
1915+
let b = combineTypes CombineOther (setTypeAttrs b2 a2') (setTypeAttrs b2 a3') in
1916+
let q = cabsAddAttributes q2 q3 in
1917+
TPtr (cabsTypeAddAttributes q b, [])
18991918
with Failure msg -> begin
19001919
ignore (warn "A.QUESTION: %a does not match %a (%s)"
19011920
d_type (unrollType t2) d_type (unrollType t3) msg);
@@ -2443,6 +2462,7 @@ let rec doSpecList (suggestedAnonName: string) (* This string will be part of
24432462
| [A.Tfloat128] -> TFloat(FLongDouble, []) (* TODO: Correct? *)
24442463

24452464
(* Now the other type specifiers *)
2465+
| [A.Tdefault] -> E.s (error "Default outside generic associations")
24462466
| [A.Tnamed n] -> begin
24472467
if n = "__builtin_va_list" &&
24482468
!Machdep.theMachine.Machdep.__builtin_va_list then begin
@@ -2629,7 +2649,7 @@ let rec doSpecList (suggestedAnonName: string) (* This string will be part of
26292649
and convertCVtoAttr (src: A.cvspec list) : A.attribute list =
26302650
match src with
26312651
| [] -> []
2632-
| CV_CONST :: tl -> ("const",[]) :: (convertCVtoAttr tl)
2652+
| CV_CONST :: tl -> ("const",[]) :: ("pconst",[]) :: (convertCVtoAttr tl)
26332653
| CV_VOLATILE :: tl -> ("volatile",[]) :: (convertCVtoAttr tl)
26342654
| CV_RESTRICT :: tl -> ("restrict",[]) :: (convertCVtoAttr tl)
26352655
| CV_COMPLEX :: tl -> ("complex",[]) :: (convertCVtoAttr tl)
@@ -4798,6 +4818,35 @@ and doExp (asconst: bool) (* This expression is used as a constant *)
47984818

47994819
| A.EXPR_PATTERN _ -> E.s (E.bug "EXPR_PATTERN in cabs2cil input")
48004820

4821+
| A.GENERIC (e, al) ->
4822+
let is_default = function
4823+
| ([SpecType Tdefault], JUSTBASE) -> true (* exactly matches cparser *)
4824+
| _ -> false
4825+
in
4826+
let (al_default, al_nondefault) = List.partition (fun (at, _) -> is_default at) al in
4827+
4828+
let typ_compatible t1 t2 =
4829+
match combineTypes CombineOther t1 t2 with (* combineTypes does "compatible types" check *)
4830+
| _ -> true
4831+
| exception (Failure _) -> false
4832+
in
4833+
let (_, _, e_typ) = doExp false e (AExp None) in (* doExp with AExp handles array and function types for "lvalue conversions" (AType would not!) *)
4834+
let e_typ = removeOuterQualifierAttributes e_typ in (* removeOuterQualifierAttributes handles qualifiers for "lvalue conversions" *)
4835+
let al_compatible = List.filter (fun ((ast, adt), _) -> typ_compatible e_typ (doOnlyType ast adt)) al_nondefault in
4836+
4837+
(* TODO: error when multiple compatible associations or defaults even when unused? *)
4838+
4839+
begin match al_compatible with
4840+
| [(_, ae)] -> doExp false ae (AExp None)
4841+
| [] ->
4842+
begin match al_default with
4843+
| [(_, ae)] -> doExp false ae (AExp None)
4844+
| [] -> E.s (error "No compatible associations or default in generic")
4845+
| _ -> E.s (error "Multiple defaults in generic")
4846+
end
4847+
| _ -> E.s (error "Multiple compatible associations in generic")
4848+
end
4849+
48014850
with e when continueOnError -> begin
48024851
(*ignore (E.log "error in doExp (%s)" (Printexc.to_string e));*)
48034852
E.hadErrors := true;

src/frontc/cabsvisit.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,17 @@ and childrenExpression vis e =
526526
let b' = visitCabsBlock vis b in
527527
if b' != b then GNU_BODY b' else e
528528
| EXPR_PATTERN _ -> e
529+
| GENERIC (e1, al) ->
530+
let e1' = ve e1 in
531+
let al' = mapNoCopy (fun (((ast, adt), ae) as a) ->
532+
let ast' = visitCabsSpecifier vis ast in
533+
let adt' = visitCabsDeclType vis false adt in
534+
let ae' = ve ae in
535+
if ast' != ast || adt' != adt || ae' != ae then ((ast', adt'), ae') else a
536+
) al
537+
in
538+
if e1' != e1 || al' != al then GENERIC (e1', al') else e
539+
529540

530541
and visitCabsInitExpression vis (ie: init_expression) : init_expression =
531542
doVisit vis vis#vinitexpr childrenInitExpression ie

src/frontc/clexer.mll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ let init_lexicon _ =
211211
THREAD loc
212212
else
213213
IDENT ("__thread", loc));
214+
("_Generic", fun loc -> GENERIC loc);
214215
]
215216

216217
(* Mark an identifier as a type name. The old mapping is preserved and will

0 commit comments

Comments
 (0)