aboutsummaryrefslogtreecommitdiffstats
path: root/cparser/Elab.ml
diff options
context:
space:
mode:
Diffstat (limited to 'cparser/Elab.ml')
-rw-r--r--cparser/Elab.ml985
1 files changed, 666 insertions, 319 deletions
diff --git a/cparser/Elab.ml b/cparser/Elab.ml
index a1f85886..718261b4 100644
--- a/cparser/Elab.ml
+++ b/cparser/Elab.ml
@@ -69,6 +69,20 @@ let top_declarations = ref ([] : globdecl list)
let top_environment = ref Env.empty
+(* Set of all globals with a definitions *)
+
+module StringSet = Set.Make(String)
+
+let global_defines = ref StringSet.empty
+
+let add_global_define loc name =
+ if StringSet.mem name !global_defines then
+ error loc "redefinition of '%s'" name;
+ global_defines := StringSet.add name !global_defines
+
+let is_global_defined name =
+ StringSet.mem name !global_defines
+
let emit_elab ?(debuginfo = true) ?(linkage = false) env loc td =
let loc = elab_loc loc in
let dec ={ gdesc = td; gloc = loc } in
@@ -84,7 +98,7 @@ let emit_elab ?(debuginfo = true) ?(linkage = false) env loc td =
| _ -> ()
end
-let reset() = top_declarations := []; top_environment := Env.empty
+let reset() = top_declarations := []; top_environment := Env.empty; global_defines := StringSet.empty
let elaborated_program () =
let p = !top_declarations in
@@ -101,6 +115,15 @@ let rec mmap f env = function
let (tl', env2) = mmap f env1 tl in
(hd' :: tl', env2)
+let rec mmap2 f env l1 l2 =
+ match l1,l2 with
+ | [],[] -> [],env
+ | a1::l1,a2::l2 ->
+ let hd,env1 = f env a1 a2 in
+ let tl,env2 = mmap2 f env1 l1 l2 in
+ (hd::tl,env2)
+ | _, _ -> invalid_arg "mmap2"
+
(* To detect redefinitions within the same scope *)
let previous_def fn env arg =
@@ -116,6 +139,13 @@ let redef fn env arg =
(* Auxiliaries for handling declarations and redeclarations *)
+let name_of_storage_class = function
+ | Storage_default -> "<default>"
+ | Storage_extern -> "'extern'"
+ | Storage_static -> "'static'"
+ | Storage_auto -> "'auto'"
+ | Storage_register -> "'register'"
+
let combine_toplevel_definitions loc env s old_sto old_ty sto ty =
let new_ty =
match combine_types AttrCompat env old_ty ty with
@@ -126,26 +156,43 @@ let combine_toplevel_definitions loc env s old_sto old_ty sto ty =
"redefinition of '%s' with a different type: %a vs %a"
s (print_typ env) old_ty (print_typ env) ty;
ty in
+ if is_global_defined s then begin
+ let old_attrs = attributes_of_type env old_ty
+ and new_attrs = attributes_of_type env ty in
+ if not (Cutil.incl_attributes new_attrs old_attrs) then
+ warning loc Ignored_attributes "attribute declaration must precede definition"
+ end;
let new_sto =
(* The only case not allowed is removing static *)
match old_sto,sto with
| Storage_static,Storage_static
| Storage_extern,Storage_extern
- | Storage_register,Storage_register
| Storage_default,Storage_default -> sto
| _,Storage_static ->
error loc "static declaration of '%s' follows non-static declaration" s;
sto
| Storage_static,_ -> Storage_static (* Static stays static *)
| Storage_extern,_ -> sto
- | Storage_default,Storage_extern -> Storage_extern
+ | Storage_default,Storage_extern ->
+ if is_global_defined s && is_function_type env ty then
+ warning loc Extern_after_definition "this extern declaration follows a non-extern definition and is ignored";
+ Storage_extern
| _,Storage_extern -> old_sto
+ (* "auto" and "register" don't appear in toplevel definitions.
+ Normally this was checked earlier. Generate error message
+ instead of "assert false", just in case. *)
+ | _,Storage_auto
+ | Storage_auto,_
| _,Storage_register
- | Storage_register,_ -> Storage_register
+ | Storage_register,_ ->
+ error loc "unexpected %s declaration of '%s'"
+ (name_of_storage_class sto) s;
+ sto
in
(new_sto, new_ty)
-let enter_or_refine_ident local loc env s sto ty =
+let enter_or_refine_ident_base local loc env new_id sto ty =
+ let s = new_id.C.name in
(* Check for illegal redefinitions:
- a name that was previously a typedef
- a variable that was already declared in the same local scope
@@ -155,41 +202,67 @@ let enter_or_refine_ident local loc env s sto ty =
if redef Env.lookup_typedef env s then
error loc "redefinition of '%s' as different kind of symbol" s;
begin match previous_def Env.lookup_ident env s with
- | Some(id, Env.II_ident(old_sto, old_ty))
- when local && Env.in_current_scope env id
+ | Some(old_id, Env.II_ident(old_sto, old_ty))
+ when local && Env.in_current_scope env old_id
&& not (sto = Storage_extern && old_sto = Storage_extern) ->
error loc "redefinition of '%s'" s
- | Some(id, Env.II_enum _) when Env.in_current_scope env id ->
+ | Some(old_id, Env.II_enum _) when Env.in_current_scope env old_id ->
error loc "redefinition of '%s' as different kind of symbol" s;
| _ ->
()
end;
- (* For a block-scoped, non-"extern" variable, a new declaration
- is entered, and it has no linkage. *)
- if local && sto <> Storage_extern then begin
- let (id, env') = Env.enter_ident env s sto ty in
- (id, sto, env', ty, false)
+ (* For a block-scoped, "static" or "auto" or "register" variable,
+ a new declaration is entered, and it has no linkage. *)
+ if local
+ && (sto = Storage_auto || sto = Storage_register || sto = Storage_static)
+ then begin
+ (new_id, sto, Env.add_ident env new_id sto ty, ty, false)
end else begin
(* For a file-scoped or "extern" variable, we need to check against
prior declarations of this variable with internal or external linkage.
The variable has linkage. *)
match previous_def Env.lookup_ident !top_environment s with
- | Some(id, Env.II_ident(old_sto, old_ty)) ->
+ | Some(old_id, Env.II_ident(old_sto, old_ty)) ->
let (new_sto, new_ty) =
combine_toplevel_definitions loc env s old_sto old_ty sto ty in
- (id, new_sto, Env.add_ident env id new_sto new_ty, new_ty, true)
+ (old_id, new_sto, Env.add_ident env old_id new_sto new_ty, new_ty, true)
| _ ->
- let (id, env') = Env.enter_ident env s sto ty in
- (id, sto, env', ty, true)
+ (new_id, sto, Env.add_ident env new_id sto ty, ty, true)
end
+(* We use two variants of [enter_or_refine]:
+
+ - [enter_or_refine_ident] is to be used for all declarations,
+ block-scoped ([local = true]) or top-level ([local = false]).
+ The name of the declared thing is given as a string [s]. If a
+ previous declaration with this name exists in the current scope,
+ its unique identifier is returned. Otherwise a fresh identifier
+ named [s] is created in the current scope of [env] and returned.
+
+ - [enter_or_refine_function] is to be used for function definitions.
+ Such definitions are always global, hence the [local] parameter
+ defaults to [false] and is omitted. The function name is given
+ as an identifier, created in advance by the caller. This
+ identifier is used if no previous declaration exists for the
+ function. Otherwise, the identifier of the previous declaration is
+ used. By creating the identifier in advance, [elab_fundef] makes
+ sure that it is properly scoped to file scope and not to the local
+ scope of function parameters.
+*)
+
+let enter_or_refine_ident local loc env s sto ty =
+ enter_or_refine_ident_base local loc env (Env.fresh_ident s) sto ty
+
+let enter_or_refine_function loc env id sto ty =
+ enter_or_refine_ident_base false loc env id sto ty
+
(* Forward declarations *)
let elab_expr_f : (cabsloc -> Env.t -> Cabs.expression -> C.exp * Env.t) ref
= ref (fun _ _ _ -> assert false)
-let elab_funbody_f : (C.typ -> bool -> Env.t -> statement -> C.stmt) ref
- = ref (fun _ _ _ _ -> assert false)
+let elab_funbody_f : (C.typ -> bool -> bool -> Env.t -> statement -> C.stmt) ref
+ = ref (fun _ _ _ _ _ -> assert false)
(** * Elaboration of constants - C99 section 6.4.4 *)
@@ -422,35 +495,53 @@ let elab_gcc_attr loc env = function
let is_power_of_two n = n > 0L && Int64.logand n (Int64.pred n) = 0L
-let extract_alignas loc a =
+(* Check alignment parameter *)
+let check_alignment loc n =
+ if not (is_power_of_two n || n = 0L) then begin
+ error loc "requested alignment %Ld is not a power of 2" n; false
+ end else
+ if n <> Int64.of_int (Int64.to_int n) then begin
+ error loc "requested alignment %Ld is too large" n; false
+ end else
+ true
+
+(* Process GCC attributes that have special significance. Currently we
+ have two: "aligned" and "packed". *)
+let enter_gcc_attr loc a =
match a with
| Attr(("aligned"|"__aligned__"), args) ->
begin match args with
- | [AInt n] when is_power_of_two n -> AAlignas (Int64.to_int n)
- | [AInt n] -> error loc "requested alignment is not a power of 2"; a
- | [_] -> error loc "requested alignment is not an integer constant"; a
- | [] -> a (* Use the default alignment as the gcc does *)
- | _ -> error loc "'aligned' attribute takes no more than 1 argument"; a
+ | [AInt n] -> if check_alignment loc n then [a] else []
+ | [_] -> error loc "requested alignment is not an integer constant"; []
+ | [] -> [] (* Use default alignment, like gcc does *)
+ | _ -> error loc "'aligned' attribute takes no more than 1 argument"; []
+ end
+ | Attr(("packed"|"__packed__"), args) ->
+ begin match args with
+ | [] -> [a]
+ | [AInt n] -> if check_alignment loc n then [a] else []
+ | [AInt n; AInt p] ->
+ if check_alignment loc n && check_alignment loc p then [a] else []
+ | [AInt n; AInt p; AInt q] when q = 0L || q = 1L ->
+ if check_alignment loc n && check_alignment loc p then [a] else []
+ | _ -> error loc "ill-formed 'packed' attribute"; []
end
- | _ -> a
+ | _ -> [a]
let elab_attribute env = function
| GCC_ATTR (l, loc) ->
List.fold_left add_attributes []
- (List.map (fun attr -> [attr])
- (List.map (extract_alignas loc)
- (List.flatten
- (List.map (elab_gcc_attr loc env) l))))
+ (List.map (enter_gcc_attr loc)
+ (List.flatten
+ (List.map (elab_gcc_attr loc env) l)))
| PACKED_ATTR (args, loc) ->
- [Attr("__packed__", List.map (elab_attr_arg loc env) args)]
+ enter_gcc_attr loc
+ (Attr("__packed__", List.map (elab_attr_arg loc env) args))
| ALIGNAS_ATTR ([a], loc) ->
+ warning loc Celeven_extension "'_Alignas' is a C11 extension";
begin match elab_attr_arg loc env a with
| AInt n ->
- if is_power_of_two n then
- [AAlignas (Int64.to_int n)]
- else begin
- error loc "requested alignment is not a power of 2"; []
- end
+ if check_alignment loc n then [AAlignas (Int64.to_int n)] else []
| _ -> error loc "requested alignment is not an integer constant"; []
| exception Wrong_attr_arg -> error loc "bad _Alignas value"; []
end
@@ -460,6 +551,22 @@ let elab_attribute env = function
let elab_attributes env al =
List.fold_left add_attributes [] (List.map (elab_attribute env) al)
+(* Warning for alignment requests that reduce the alignment below the
+ natural alignment. *)
+
+let warn_if_reduced_alignment loc ~actual ~natural =
+ match actual, natural with
+ | Some act, Some nat when act < nat ->
+ warning loc Reduced_alignment
+ "requested alignment (%d) is less than natural alignment (%d)"
+ act nat
+ | _, _ -> ()
+
+let check_reduced_alignment loc env typ =
+ warn_if_reduced_alignment loc
+ ~actual: (wrap alignof loc env typ)
+ ~natural: (wrap alignof loc env (erase_attributes_type env typ))
+
(* Auxiliary for typespec elaboration *)
let typespec_rank = function (* Don't change this *)
@@ -491,14 +598,15 @@ let get_nontype_attrs env ty =
let nta = List.filter to_be_removed (attributes_of_type env ty) in
(remove_attributes_type env nta ty, nta)
-(* Elaboration of a type specifier. Returns 5-tuple:
- (storage class, "inline" flag, "typedef" flag, elaborated type, new env)
+(* Elaboration of a type specifier. Returns 6-tuple:
+ (storage class, "inline" flag, "noreturn" flag, "typedef" flag,
+ elaborated type, new env)
Optional argument "only" is true if this is a standalone
struct or union declaration, without variable names.
C99 section 6.7.2.
*)
-let rec elab_specifier keep_ty ?(only = false) loc env specifier =
+let rec elab_specifier ?(only = false) loc env specifier =
(* We first divide the parts of the specifier as follows:
- a storage class
- a set of attributes (const, volatile, restrict)
@@ -506,18 +614,20 @@ let rec elab_specifier keep_ty ?(only = false) loc env specifier =
let sto = ref Storage_default
and inline = ref false
and noreturn = ref false
+ and restrict = ref false
and attr = ref []
and tyspecs = ref []
and typedef = ref false in
let do_specifier = function
| SpecCV cv ->
+ restrict := cv = CV_RESTRICT;
attr := add_attributes (elab_cvspec env cv) !attr
| SpecStorage st ->
if !sto <> Storage_default && st <> TYPEDEF then
- error loc "multiple storage-classes in declaration specifier";
+ error loc "multiple storage classes in declaration specifier";
begin match st with
- | AUTO -> ()
+ | AUTO -> sto := Storage_auto
| STATIC -> sto := Storage_static
| EXTERN -> sto := Storage_extern
| REGISTER -> sto := Storage_register
@@ -530,9 +640,18 @@ let rec elab_specifier keep_ty ?(only = false) loc env specifier =
| SpecFunction NORETURN -> noreturn := true
| SpecType tys -> tyspecs := tys :: !tyspecs in
+ let restrict_check ty =
+ if !restrict then
+ if not (is_pointer_type env ty) then
+ error loc "restrict requires a pointer type (%a is invalid)" (print_typ env) ty
+ else if is_function_pointer_type env ty then
+ error loc "pointer to function type %a may not be 'restrict' qualified" (print_typ env) ty in
+
List.iter do_specifier specifier;
- let simple ty = (!sto, !inline, !noreturn ,!typedef, add_attributes_type !attr ty, env) in
+ let simple ty =
+ restrict_check ty;
+ (!sto, !inline, !noreturn ,!typedef, add_attributes_type !attr ty, env) in
(* As done in CIL, partition !attr into struct-related attributes,
which are returned, and other attributes, which are left in !attr.
@@ -603,22 +722,28 @@ let rec elab_specifier keep_ty ?(only = false) loc env specifier =
let a' =
add_attributes (get_struct_attrs()) (elab_attributes env a) in
let (id', env') =
- elab_struct_or_union keep_ty only Struct loc id optmembers a' env in
- (!sto, !inline, !noreturn, !typedef, TStruct(id', !attr), env')
+ elab_struct_or_union only Struct loc id optmembers a' env in
+ let ty = TStruct(id', !attr) in
+ restrict_check ty;
+ (!sto, !inline, !noreturn, !typedef, ty, env')
| [Cabs.Tstruct_union(UNION, id, optmembers, a)] ->
let a' =
add_attributes (get_struct_attrs()) (elab_attributes env a) in
let (id', env') =
- elab_struct_or_union keep_ty only Union loc id optmembers a' env in
- (!sto, !inline, !noreturn, !typedef, TUnion(id', !attr), env')
+ elab_struct_or_union only Union loc id optmembers a' env in
+ let ty = TUnion(id', !attr) in
+ restrict_check ty;
+ (!sto, !inline, !noreturn, !typedef, ty, env')
| [Cabs.Tenum(id, optmembers, a)] ->
let a' =
add_attributes (get_struct_attrs()) (elab_attributes env a) in
let (id', env') =
elab_enum only loc id optmembers a' env in
- (!sto, !inline, !noreturn, !typedef, TEnum(id', !attr), env')
+ let ty = TEnum (id', !attr) in
+ restrict_check ty;
+ (!sto, !inline, !noreturn, !typedef, ty, env')
(* Specifier doesn't make sense *)
| _ ->
@@ -644,12 +769,23 @@ and elab_return_type loc env ty =
error loc "function cannot return function type %a" (print_typ env) ty
| _ -> ()
-and elab_type_declarator keep_ty loc env ty kr_ok = function
+(* The [?fundef] parameter is true if we're elaborating a function definition
+ and false otherwise. When [fundef = true], K&R function declarators
+ are allowed, and the returned environment includes bindings for the
+ function parameters and the struct/unions they may define.
+ When [fundef = false], K&R function declarators are rejected
+ and declarations in parameters are not returned. *)
+
+and elab_type_declarator ?(fundef = false) loc env ty = function
| Cabs.JUSTBASE ->
((ty, None), env)
| Cabs.ARRAY(d, cv_specs, sz) ->
let (ty, a) = get_nontype_attrs env ty in
let a = add_attributes a (elab_cvspecs env cv_specs) in
+ if wrap incomplete_type loc env ty then
+ error loc "array type has incomplete element type %a" (print_typ env) ty;
+ if wrap contains_flex_array_mem loc env ty then
+ warning loc Flexible_array_extensions "%a may not be used as an array element due to flexible array member" (print_typ env) ty;
let sz' =
match sz with
| None ->
@@ -666,33 +802,45 @@ and elab_type_declarator keep_ty loc env ty kr_ok = function
| None ->
error loc "size of array is not a compile-time constant";
Some 1L in (* produces better error messages later *)
- elab_type_declarator keep_ty loc env (TArray(ty, sz', a)) kr_ok d
+ elab_type_declarator ~fundef loc env (TArray(ty, sz', a)) d
| Cabs.PTR(cv_specs, d) ->
let (ty, a) = get_nontype_attrs env ty in
let a = add_attributes a (elab_cvspecs env cv_specs) in
- elab_type_declarator keep_ty loc env (TPtr(ty, a)) kr_ok d
+ if is_function_type env ty && incl_attributes [ARestrict] a then
+ error loc "pointer to function type %a may not be 'restrict' qualified" (print_typ env) ty;
+ elab_type_declarator ~fundef loc env (TPtr(ty, a)) d
| Cabs.PROTO(d, (params, vararg)) ->
elab_return_type loc env ty;
let (ty, a) = get_nontype_attrs env ty in
- let params',env' = elab_parameters keep_ty env params in
- let env = if keep_ty then Env.add_types env env' else env in
- elab_type_declarator keep_ty loc env (TFun(ty, Some params', vararg, a)) kr_ok d
+ let (params', env') = elab_parameters env params in
+ (* For a function declaration (fundef = false), the scope introduced
+ to treat parameters ends here, so we discard the extended
+ environment env' returned by elab_parameters.
+ For a function definition (fundef = true) we return the
+ extended environment env' so that it can serve as the basis
+ to elaborating the function body. *)
+ let env'' = if fundef then env' else env in
+ elab_type_declarator ~fundef loc env'' (TFun(ty, Some params', vararg, a)) d
| Cabs.PROTO_OLD(d, params) ->
elab_return_type loc env ty;
let (ty, a) = get_nontype_attrs env ty in
+ (* For consistency with the PROTO case above, for a function definition
+ (fundef = true) we open a new scope, even though we do not
+ add any bindings for the parameters. *)
+ let env'' = if fundef then Env.new_scope env else env in
match params with
| [] ->
- elab_type_declarator keep_ty loc env (TFun(ty, None, false, a)) kr_ok d
+ elab_type_declarator ~fundef loc env'' (TFun(ty, None, false, a)) d
| _ ->
- if not kr_ok || d <> Cabs.JUSTBASE then
+ if not fundef || d <> Cabs.JUSTBASE then
fatal_error loc "illegal old-style K&R function definition";
- ((TFun(ty, None, false, a), Some params), env)
+ ((TFun(ty, None, false, a), Some params), env'')
(* Elaboration of parameters in a prototype *)
-and elab_parameters keep_ty env params =
+and elab_parameters env params =
(* Prototype introduces a new scope *)
- let (vars, env) = mmap (elab_parameter keep_ty) (Env.new_scope env) params in
+ let (vars, env) = mmap elab_parameter (Env.new_scope env) params in
(* Catch special case f(t) where t is void or a typedef to void *)
match vars with
| [ ( {C.name=""}, t) ] when is_void_type env t -> [],env
@@ -700,42 +848,62 @@ and elab_parameters keep_ty env params =
(* Elaboration of a function parameter *)
-and elab_parameter keep_ty env (PARAM (spec, id, decl, attr, loc)) =
- let (sto, inl, noret, tydef, bty, env1) = elab_specifier keep_ty loc env spec in
+and elab_parameter env (PARAM (spec, id, decl, attr, loc)) =
+ let (sto, inl, noret, tydef, bty, env1) = elab_specifier loc env spec in
if tydef then
error loc "'typedef' used in function parameter";
- let ((ty, _), _) = elab_type_declarator keep_ty loc env1 bty false decl in
+ let ((ty, _), _) = elab_type_declarator loc env1 bty decl in
let ty = add_attributes_type (elab_attributes env attr) ty in
if sto <> Storage_default && sto <> Storage_register then
- error loc
- "invalid storage-class specifier in function declarator";
+ error loc (* NB: 'auto' not allowed *)
+ "invalid storage class %s for function parameter"
+ (name_of_storage_class sto);
if inl then
error loc "'inline' can only appear on functions";
if noret then
error loc "'_Noreturn' can only appear on functions";
let id = match id with None -> "" | Some id -> id in
+ if id <> "" && is_void_type env1 ty then
+ error loc "argument '%s' may not have 'void' type" id;
if id <> "" && redef Env.lookup_ident env id then
error loc "redefinition of parameter '%s'" id;
(* replace array and function types by pointer types *)
let ty1 = argument_conversion env1 ty in
+ if is_qualified_array ty1 then
+ error loc "type qualifier used in non-outermost array type derivation";
+ if has_std_alignas env ty then begin
+ if id <> "" then
+ error loc "alignment specified for parameter '%s'" id
+ else
+ error loc "alignment specified for unnamed parameter"
+ end;
let (id', env2) = Env.enter_ident env1 id sto ty1 in
( (id', ty1) , env2)
-(* Elaboration of a (specifier, Cabs "name") pair *)
-
-and elab_fundef_name keep_ty env spec (Name (id, decl, attr, loc)) =
- let (sto, inl, noret, tydef, bty, env') = elab_specifier keep_ty loc env spec in
+(* Elaboration of a (specifier, Cabs "name") pair as found in a function
+ definition. Returns two environments: the first is [env]
+ enriched with struct/union definitions from the return type,
+ as usual; the second is like the first, plus a new scope.
+ For a prototyped function ([kr_params = None]) the new scope
+ includes bindings for the function parameters and the struct/unions
+ they may define. For a K&R function ([kr_params <> None]) the new
+ scope is empty. *)
+
+and elab_fundef_name env spec (Name (s, decl, attr, loc)) =
+ let (sto, inl, noret, tydef, bty, env') = elab_specifier loc env spec in
if tydef then
error loc "'typedef' is forbidden here";
- let ((ty, kr_params), env'') = elab_type_declarator keep_ty loc env' bty true decl in
+ let id = Env.fresh_ident s in
+ let ((ty, kr_params), env'') =
+ elab_type_declarator ~fundef:true loc env' bty decl in
let a = elab_attributes env attr in
- (id, sto, inl, noret, add_attributes_type a ty, kr_params, env'')
+ (id, sto, inl, noret, add_attributes_type a ty, kr_params, env', env'')
(* Elaboration of a name group. C99 section 6.7.6 *)
-and elab_name_group keep_ty loc env (spec, namelist) =
+and elab_name_group loc env (spec, namelist) =
let (sto, inl, noret, tydef, bty, env') =
- elab_specifier keep_ty loc env spec in
+ elab_specifier loc env spec in
if tydef then
error loc "'typedef' is forbidden here";
if inl then
@@ -744,35 +912,40 @@ and elab_name_group keep_ty loc env (spec, namelist) =
error loc "'_Noreturn' is forbidden here";
let elab_one_name env (Name (id, decl, attr, loc)) =
let ((ty, _), env1) =
- elab_type_declarator keep_ty loc env bty false decl in
+ elab_type_declarator loc env bty decl in
let a = elab_attributes env attr in
((id, add_attributes_type a ty), env1) in
(mmap elab_one_name env' namelist, sto)
(* Elaboration of an init-name group *)
-and elab_init_name_group keep_ty loc env (spec, namelist) =
+and elab_init_name_group loc env (spec, namelist) =
let (sto, inl, noret, tydef, bty, env') =
- elab_specifier keep_ty ~only:(namelist=[]) loc env spec in
+ elab_specifier ~only:(namelist=[]) loc env spec in
+ if noret && tydef then
+ error loc "'_Noreturn' can only appear on functions";
let elab_one_name env (Init_name (Name (id, decl, attr, loc), init)) =
let ((ty, _), env1) =
- elab_type_declarator keep_ty loc env bty false decl in
+ elab_type_declarator loc env bty decl in
let a = elab_attributes env attr in
- if inl && not (is_function_type env ty) then
+ let has_fun_typ = is_function_type env ty in
+ if inl && not has_fun_typ then
error loc "'inline' can only appear on functions";
let a' =
if noret then begin
warning loc Celeven_extension "_Noreturn functions are a C11 extension";
- if not (is_function_type env ty) then
+ if not has_fun_typ then
error loc "'_Noreturn' can only appear on functions";
add_attributes [Attr("noreturn",[])] a
end else a in
+ if has_std_alignas env ty && has_fun_typ then
+ error loc "alignment specified for function '%s'" id;
((id, add_attributes_type a' ty, init), env1) in
(mmap elab_one_name env' namelist, sto, tydef)
(* Elaboration of a field group *)
-and elab_field_group keep_ty env (Field_group (spec, fieldlist, loc)) =
+and elab_field_group env (Field_group (spec, fieldlist, loc)) =
let fieldlist = List.map
(function (None, x) -> (Name ("", JUSTBASE, [], loc), x)
@@ -781,7 +954,7 @@ and elab_field_group keep_ty env (Field_group (spec, fieldlist, loc)) =
in
let ((names, env'), sto) =
- elab_name_group keep_ty loc env (spec, List.map fst fieldlist) in
+ elab_name_group loc env (spec, List.map fst fieldlist) in
if sto <> Storage_default then
error loc "non-default storage in struct or union";
@@ -789,10 +962,10 @@ and elab_field_group keep_ty env (Field_group (spec, fieldlist, loc)) =
(* This should actually never be triggered, empty structs are captured earlier *)
warning loc Missing_declarations "declaration does not declare anything";
- let elab_bitfield (Name (_, _, _, loc), optbitsize) (id, ty) =
- let optbitsize' =
+ let elab_bitfield env (Name (_, _, _, loc), optbitsize) (id, ty) =
+ let optbitsize',env' =
match optbitsize with
- | None -> None
+ | None -> None,env
| Some sz ->
let ik =
match unroll env' ty with
@@ -802,40 +975,45 @@ and elab_field_group keep_ty env (Field_group (spec, fieldlist, loc)) =
if integer_rank ik > integer_rank IInt then begin
error loc
"the type of bit-field '%a' must be an integer type no bigger than 'int'" pp_field id;
- None
+ None,env
+ end else if has_std_alignas env' ty then begin
+ error loc "alignment specified for bit-field '%a'" pp_field id;
+ None, env
end else begin
let expr,env' =(!elab_expr_f loc env sz) in
match Ceval.integer_expr env' expr with
| Some n ->
if n < 0L then begin
error loc "bit-field '%a' has negative width (%Ld)" pp_field id n;
- None
+ None,env
end else
let max = Int64.of_int(sizeof_ikind ik * 8) in
if n > max then begin
error loc "size of bit-field '%a' (%Ld bits) exceeds its type (%Ld bits)" pp_field id n max;
- None
+ None,env
end else
if n = 0L && id <> "" then begin
error loc "named bit-field '%a' has zero width" pp_field id;
- None
+ None,env
end else
- Some(Int64.to_int n)
+ Some(Int64.to_int n),env'
| None ->
error loc "bit-field '%a' width not an integer constant" pp_field id;
- None
+ None,env
end in
+ if is_qualified_array ty then
+ error loc "type qualifier used in array declarator outside of function prototype";
let anon_composite = is_anonymous_composite ty in
if id = "" && not anon_composite && optbitsize = None then
warning loc Missing_declarations "declaration does not declare anything";
- { fld_name = id; fld_typ = ty; fld_bitfield = optbitsize'; fld_anonymous = id = "" && anon_composite}
+ { fld_name = id; fld_typ = ty; fld_bitfield = optbitsize'; fld_anonymous = id = "" && anon_composite},env'
in
- (List.map2 elab_bitfield fieldlist names, env')
+ (mmap2 elab_bitfield env' fieldlist names)
(* Elaboration of a struct or union. C99 section 6.7.2.1 *)
-and elab_struct_or_union_info keep_ty kind loc env members attrs =
- let (m, env') = mmap (elab_field_group keep_ty) env members in
+and elab_struct_or_union_info kind loc env members attrs =
+ let (m, env') = mmap elab_field_group env members in
let m = List.flatten m in
let m,_ = mmap (fun c fld ->
if fld.fld_anonymous then
@@ -866,16 +1044,22 @@ and elab_struct_or_union_info keep_ty kind loc env members attrs =
duplicate acc rest in
duplicate [] m;
(* Check for incomplete types *)
- let rec check_incomplete = function
+ let rec check_incomplete only = function
| [] -> ()
- | [ { fld_typ = TArray(ty_elt, None, _) } ] when kind = Struct -> ()
- (* C99: ty[] allowed as last field of a struct *)
+ | [ { fld_typ = TArray(ty_elt, None, _) as typ; fld_name = name } ] when kind = Struct ->
+ (* C99: ty[] allowed as last field of a struct, provided this is not the only field *)
+ if only then
+ error loc "flexible array member '%a' not allowed in otherwise empty struct" pp_field name;
+ check_reduced_alignment loc env' typ
| fld :: rem ->
if wrap incomplete_type loc env' fld.fld_typ then
(* Must be fatal otherwise we get problems constructing the init *)
fatal_error loc "member '%a' has incomplete type" pp_field fld.fld_name;
- check_incomplete rem in
- check_incomplete m;
+ if wrap contains_flex_array_mem loc env' fld.fld_typ && kind = Struct then
+ warning loc Flexible_array_extensions "%a may not be used as a struct member due to flexible array member" (print_typ env) fld.fld_typ;
+ check_reduced_alignment loc env' fld.fld_typ;
+ check_incomplete false rem in
+ check_incomplete true m;
(* Warn for empty structs or unions *)
if m = [] then
if kind = Struct then begin
@@ -883,16 +1067,26 @@ and elab_struct_or_union_info keep_ty kind loc env members attrs =
end else begin
fatal_error loc "empty union is a GNU extension"
end;
+ let ci = composite_info_def env' kind attrs m in
+ (* Warn for reduced alignment *)
+ if attrs <> [] then begin
+ let ci_nat = composite_info_def env' kind [] m in
+ warn_if_reduced_alignment loc
+ ~actual:ci.Env.ci_alignof ~natural:ci_nat.Env.ci_alignof
+ end;
+ (* Final result *)
(composite_info_def env' kind attrs m, env')
-and elab_struct_or_union keep_ty only kind loc tag optmembers attrs env =
+and elab_struct_or_union only kind loc tag optmembers attrs env =
let warn_attrs () =
if attrs <> [] then
- warning loc Unnamed "attribute declaration must precede definition" in
+ warning loc Ignored_attributes "attribute declaration must precede definition" in
let optbinding, tag =
match tag with
| None -> None, ""
| Some s ->
+ if redef Env.lookup_enum env s then
+ error loc "'%s' redeclared as different kind of symbol" s;
Env.lookup_composite env s, s
in
match optbinding, optmembers with
@@ -910,9 +1104,9 @@ and elab_struct_or_union keep_ty only kind loc tag optmembers attrs env =
| Some(tag', ({Env.ci_sizeof = None} as ci)), Some members
when Env.in_current_scope env tag' ->
if ci.Env.ci_kind <> kind then
- error loc "use of '%s' with tag type that does not match previous declaration" tag;
+ fatal_error loc "use of '%s' with tag type that does not match previous declaration" tag;
(* finishing the definition of an incomplete struct or union *)
- let (ci', env') = elab_struct_or_union_info keep_ty kind loc env members attrs in
+ let (ci', env') = elab_struct_or_union_info kind loc env members attrs in
(* Emit a global definition for it *)
emit_elab env' loc (Gcompositedef(kind, tag', attrs, ci'.Env.ci_members));
(* Replace infos but keep same ident *)
@@ -939,7 +1133,7 @@ and elab_struct_or_union keep_ty only kind loc tag optmembers attrs env =
emit_elab env' loc (Gcompositedecl(kind, tag', attrs));
(* elaborate the members *)
let (ci2, env'') =
- elab_struct_or_union_info keep_ty kind loc env' members attrs in
+ elab_struct_or_union_info kind loc env' members attrs in
(* emit a definition *)
emit_elab env'' loc (Gcompositedef(kind, tag', attrs, ci2.Env.ci_members));
(* Replace infos but keep same ident *)
@@ -965,7 +1159,7 @@ and elab_enum_item env ((s, exp), loc) nextval =
if redef Env.lookup_typedef env s then
error loc "'%s' redeclared as different kind of symbol" s;
if not (int_representable v (8 * sizeof_ikind enum_ikind) (is_signed_ikind enum_ikind)) then
- warning loc Constant_conversion "integer literal '%Ld' is too large to be represented in any integer type"
+ warning loc Constant_conversion "integer literal '%Ld' is too large to be represented in the enumeration integer type"
v;
let (id, env') = Env.enter_enum_item env s v in
((id, v, exp'), Int64.succ v, env')
@@ -973,10 +1167,15 @@ and elab_enum_item env ((s, exp), loc) nextval =
(* Elaboration of an enumeration declaration. C99 section 6.7.2.2 *)
and elab_enum only loc tag optmembers attrs env =
- let tag = match tag with None -> "" | Some s -> s in
+ let tag = match tag with
+ | None -> ""
+ | Some s ->
+ if redef Env.lookup_struct env s || redef Env.lookup_union env s then
+ error loc "'%s' redeclared as different kind of symbol" s;
+ s in
match optmembers with
| None ->
- if only then
+ if only && not (redef Env.lookup_enum env tag) then
fatal_error loc
"forward declaration of 'enum %s' is not allowed in ISO C" tag;
let (tag', info) = wrap Env.lookup_enum loc env tag in (tag', env)
@@ -998,10 +1197,12 @@ and elab_enum only loc tag optmembers attrs env =
(* Elaboration of a naked type, e.g. in a cast *)
let elab_type loc env spec decl =
- let (sto, inl, noret, tydef, bty, env') = elab_specifier false loc env spec in
- let ((ty, _), env'') = elab_type_declarator false loc env' bty false decl in
+ let (sto, inl, noret, tydef, bty, env') = elab_specifier loc env spec in
+ let ((ty, _), env'') = elab_type_declarator loc env' bty decl in
+ (* NB: it seems the parser doesn't accept any of the specifiers below.
+ We keep the error message as extra safety. *)
if sto <> Storage_default || inl || noret || tydef then
- error loc "'typedef', 'extern', 'static', 'register' and 'inline' are meaningless in cast";
+ error loc "'typedef', 'extern', 'static', 'auto', 'register' and 'inline' are meaningless in cast";
(ty, env'')
@@ -1040,11 +1241,11 @@ let check_init_type loc env a ty =
else if wrap2 valid_cast loc env a.etyp ty then
if wrap2 int_pointer_conversion loc env a.etyp ty then
warning loc Int_conversion
- "incompatible integer-pointer conversion initializer has type %a instead of the expected type %a"
+ "incompatible integer-pointer conversion: initializer has type %a instead of the expected type %a"
(print_typ env) a.etyp (print_typ env) ty
else
warning loc Unnamed
- "incompatible conversion initializer has type %a instead of the expected type %a"
+ "incompatible conversion: initializer has type %a instead of the expected type %a"
(print_typ env) a.etyp (print_typ env) ty
else
error loc
@@ -1087,6 +1288,9 @@ module I = struct
(* Change the initializer for the current point *)
let set (z, i) i' = (z, i')
+ (* Is the current point at top? *)
+ let at_top = function Ztop(_, _), _ -> true | _, _ -> false
+
(* Put the current point back to the top *)
let rec to_top = function
| Ztop(name, ty), i as zi -> zi
@@ -1372,7 +1576,13 @@ and elab_single zi a il =
from the expression: do as above *)
elab_list (I.set zi (Init_single a)) il false
| TStruct _ | TUnion _ | TArray _ ->
- (* This is an aggregate: we need to drill into it, recursively *)
+ (* This is an aggregate.
+ At top, explicit { } are required. *)
+ if I.at_top zi then begin
+ error loc "invalid initializer, an initializer list was expected";
+ raise Exit
+ end;
+ (* Otherwise we need to drill into the aggregate, recursively *)
begin match I.first env zi with
| I.OK zi' ->
elab_single zi' a il
@@ -1434,23 +1644,51 @@ let elab_initializer loc env root ty ie =
(fixup_typ loc env ty init, Some init)
+(* Contexts for elaborating statements and expressions *)
+
+type elab_context = {
+ ctx_return_typ: typ; (**r return type for the function *)
+ ctx_labels: StringSet.t; (**r all labels defined in the function *)
+ ctx_break: bool; (**r is 'break' allowed? *)
+ ctx_continue: bool; (**r is 'continue' allowed? *)
+ ctx_in_switch: bool; (**r are 'case' and 'default' allowed? *)
+ ctx_vararg: bool; (**r is this a vararg function? *)
+ ctx_nonstatic_inline: bool (**r is this a nonstatic inline function? *)
+}
+
+(* Context for evaluating compile-time constant expressions.
+ Only the [ctx_vararg] and [ctx_nonstatic_inline] fields matter. *)
+let ctx_constexp = {
+ ctx_return_typ = TVoid [];
+ ctx_labels = StringSet.empty;
+ ctx_break = false; ctx_continue = false; ctx_in_switch = false;
+ ctx_vararg = false; ctx_nonstatic_inline = false
+}
+
+
(* Elaboration of expressions *)
-let elab_expr vararg loc env a =
+let elab_expr ctx loc env a =
- let err fmt = error loc fmt in (* non-fatal error *)
- let error fmt = fatal_error loc fmt in
- let warning t fmt =
- warning loc t fmt in
+ let error fmt = error loc fmt in
+ let fatal_error fmt = fatal_error loc fmt in
+ let warning t fmt = warning loc t fmt in
let check_ptr_arith env ty s =
match unroll env ty with
| TVoid _ ->
- err "illegal arithmetic on a pointer to void in binary '%c'" s
+ error "illegal arithmetic on a pointer to void in binary '%c'" s
| TFun _ ->
- err "illegal arithmetic on a pointer to the function type %a in binary '%c'" (print_typ env) ty s
+ error "illegal arithmetic on a pointer to the function type %a in binary '%c'" (print_typ env) ty s
| _ -> if incomplete_type env ty then
- err "arithmetic on a pointer to an incomplete type %a in binary '%c'" (print_typ env) ty s
+ error "arithmetic on a pointer to an incomplete type %a in binary '%c'" (print_typ env) ty s
+ in
+
+ let check_static_var id sto ty =
+ if ctx.ctx_nonstatic_inline
+ && sto = Storage_static
+ && List.mem AConst (attributes_of_type env ty)
+ then warning Static_in_inline "static variable '%s' is used in an inline function with external linkage" id.C.name
in
let rec elab env = function
@@ -1459,7 +1697,8 @@ let elab_expr vararg loc env a =
| VARIABLE s ->
begin match wrap Env.lookup_ident loc env s with
- | (id, Env.II_ident(sto, ty)) ->
+ | (id, Env.II_ident(sto, ty)) ->
+ check_static_var id sto ty;
{ edesc = EVar id; etyp = ty },env
| (id, Env.II_enum v) ->
{ edesc = EConst(CEnum(id, v)); etyp = TInt(enum_ikind, []) },env
@@ -1478,7 +1717,7 @@ let elab_expr vararg loc env a =
match (unroll env b1.etyp, unroll env b2.etyp) with
| (TPtr(t, _) | TArray(t, _, _)), (TInt _ | TEnum _) -> t
| (TInt _ | TEnum _), (TPtr(t, _) | TArray(t, _, _)) -> t
- | t1, t2 -> error "subscripted value is neither an array nor pointer" in
+ | t1, t2 -> fatal_error "subscripted value is neither an array nor pointer" in
{ edesc = EBinop(Oindex, b1, b2, TPtr(tres, [])); etyp = tres },env
| MEMBEROF(a1, fieldname) ->
@@ -1490,7 +1729,7 @@ let elab_expr vararg loc env a =
| TUnion(id, attrs) ->
(wrap Env.find_union_member loc env (id, fieldname), attrs)
| _ ->
- error "request for member '%s' in something not a structure or union" fieldname in
+ fatal_error "request for member '%s' in something not a structure or union" fieldname in
(* A field of a const/volatile struct or union is itself const/volatile *)
let rec access = function
| [] -> b1
@@ -1512,10 +1751,10 @@ let elab_expr vararg loc env a =
| TUnion(id, attrs) ->
(wrap Env.find_union_member loc env (id, fieldname), attrs)
| _ ->
- error "request for member '%s' in something not a structure or union" fieldname
+ fatal_error "request for member '%s' in something not a structure or union" fieldname
end
| _ ->
- error "member reference type %a is not a pointer" (print_typ env) b1.etyp in
+ fatal_error "member reference type %a is not a pointer" (print_typ env) b1.etyp in
let rec access = function
| [] -> assert false
| [fld] ->
@@ -1538,8 +1777,8 @@ let elab_expr vararg loc env a =
(elaboration) --> __builtin_va_arg(ap, sizeof(ty))
*)
| CALL((VARIABLE "__builtin_va_start" as a1), [a2; a3]) ->
- if not vararg then
- err "'va_start' used in function with fixed args";
+ if not ctx.ctx_vararg then
+ error "'va_start' used in function with fixed args";
let b1,env = elab env a1 in
let b2,env = elab env a2 in
let _b3,env = elab env a3 in
@@ -1584,9 +1823,9 @@ let elab_expr vararg loc env a =
| TPtr(ty, a) ->
begin match unroll env ty with
| TFun(res, args, vararg, a) -> (res, args, vararg)
- | _ -> error "called object type %a is neither a function nor function pointer" (print_typ env) b1.etyp
+ | _ -> fatal_error "called object type %a is neither a function nor function pointer" (print_typ env) b1.etyp
end
- | _ -> error "called object type %a is neither a function nor function pointer" (print_typ env) b1.etyp
+ | _ -> fatal_error "called object type %a is neither a function nor function pointer" (print_typ env) b1.etyp
in
(* Type-check the arguments against the prototype *)
let bl',env =
@@ -1608,18 +1847,18 @@ let elab_expr vararg loc env a =
if not (wrap2 valid_cast loc env b1.etyp ty) then
begin match unroll env b1.etyp, unroll env ty with
| _, (TStruct _|TUnion _ | TVoid _) ->
- err "used type %a where arithmetic or pointer type is required"
+ error "used type %a where arithmetic or pointer type is required"
(print_typ env) ty
| (TStruct _| TUnion _ | TVoid _),_ ->
- err "operand of type %a where arithmetic or pointer type is required"
+ error "operand of type %a where arithmetic or pointer type is required"
(print_typ env) b1.etyp
| TFloat _, TPtr _ ->
- err "operand of type %a cannot be cast to a pointer type"
+ error "operand of type %a cannot be cast to a pointer type"
(print_typ env) b1.etyp
| TPtr _ , TFloat _ ->
- err "pointer cannot be cast to type %a" (print_typ env) ty
+ error "pointer cannot be cast to type %a" (print_typ env) ty
| _ ->
- err "illegal cast from %a to %a" (print_typ env) b1.etyp (print_typ env) ty
+ error "illegal cast from %a to %a" (print_typ env) b1.etyp (print_typ env) ty
end;
{ edesc = ECast(ty, b1); etyp = ty },env
@@ -1629,7 +1868,7 @@ let elab_expr vararg loc env a =
let (ty, env) = elab_type loc env spec dcl in
begin match elab_initializer loc env "<compound literal>" ty ie with
| (ty', Some i) -> { edesc = ECompound(ty', i); etyp = ty' },env
- | (ty', None) -> error "ill-formed compound literal"
+ | (ty', None) -> fatal_error "ill-formed compound literal"
end
(* 6.5.3 Unary expressions *)
@@ -1637,54 +1876,39 @@ let elab_expr vararg loc env a =
| EXPR_SIZEOF a1 ->
let b1,env = elab env a1 in
if wrap incomplete_type loc env b1.etyp then
- error "invalid application of 'sizeof' to an incomplete type %a" (print_typ env) b1.etyp;
+ fatal_error "invalid application of 'sizeof' to an incomplete type %a" (print_typ env) b1.etyp;
if wrap is_bitfield loc env b1 then
- error "invalid application of 'sizeof' to a bit-field";
- let bdesc =
- (* Catch special cases sizeof("string literal") *)
- match b1.edesc with
- | EConst(CStr s) ->
- let sz = String.length s + 1 in
- EConst(CInt(Int64.of_int sz, size_t_ikind(), ""))
- | EConst(CWStr s) ->
- let sz = (!config).sizeof_wchar * (List.length s + 1) in
- EConst(CInt(Int64.of_int sz, size_t_ikind(), ""))
- | _ ->
- ESizeof b1.etyp in
- { edesc = bdesc; etyp = TInt(size_t_ikind(), []) },env
+ fatal_error "invalid application of 'sizeof' to a bit-field";
+ { edesc = ESizeof b1.etyp; etyp = TInt(size_t_ikind(), []) },env
| TYPE_SIZEOF (spec, dcl) ->
let (ty, env') = elab_type loc env spec dcl in
if wrap incomplete_type loc env' ty then
- error "invalid application of 'sizeof' to an incomplete type %a" (print_typ env) ty;
+ fatal_error "invalid application of 'sizeof' to an incomplete type %a" (print_typ env) ty;
{ edesc = ESizeof ty; etyp = TInt(size_t_ikind(), []) },env'
- | EXPR_ALIGNOF a1 ->
- let b1,env = elab env a1 in
- if wrap incomplete_type loc env b1.etyp then
- error "invalid application of '_Alignof' to an incomplete type %a" (print_typ env) b1.etyp;
- if wrap is_bitfield loc env b1 then
- error "invalid application of '_Alignof' to a bit-field";
- { edesc = EAlignof b1.etyp; etyp = TInt(size_t_ikind(), []) },env
-
- | TYPE_ALIGNOF (spec, dcl) ->
+ | ALIGNOF (spec, dcl) ->
let (ty, env') = elab_type loc env spec dcl in
+ warning Celeven_extension "'_Alignof' is a C11 extension";
if wrap incomplete_type loc env' ty then
- error "invalid application of 'alignof' to an incomplete type %a" (print_typ env) ty;
+ fatal_error "invalid application of 'alignof' to an incomplete type %a" (print_typ env) ty;
{ edesc = EAlignof ty; etyp = TInt(size_t_ikind(), []) },env'
| BUILTIN_OFFSETOF ((spec,dcl), mem) ->
let (ty,env) = elab_type loc env spec dcl in
if incomplete_type env ty then
- error "offsetof of incomplete type %a" (print_typ env) ty;
+ fatal_error "offsetof of incomplete type %a" (print_typ env) ty;
let members env ty mem =
match ty with
| TStruct (id,_) -> wrap Env.find_struct_member loc env (id,mem)
| TUnion (id,_) -> wrap Env.find_union_member loc env (id,mem)
- | _ -> error "request for member '%s' in something not a structure or union" mem in
+ | _ -> fatal_error "request for member '%s' in something not a structure or union" mem in
let rec offset_of_list acc env ty = function
| [] -> acc,ty
- | fld::rest -> let off = offsetof env ty fld in
+ | fld::rest ->
+ if fld.fld_bitfield <> None then
+ fatal_error "cannot compute offset of bit-field '%s'" fld.fld_name;
+ let off = offsetof env ty fld in
offset_of_list (acc+off) env fld.fld_typ rest in
let offset_of_member (env,off_accu,ty) mem =
match mem,unroll env ty with
@@ -1696,16 +1920,16 @@ let elab_expr vararg loc env a =
| ATINDEX_INIT e,TArray (sub_ty,_,_) ->
let e,env = elab env e in
let e = match Ceval.integer_expr env e with
- | None -> error "array element designator for is not an integer constant expression"
+ | None -> fatal_error "array element designator is not an integer constant expression"
| Some n-> n in
let size = match sizeof env sub_ty with
| None -> assert false (* We expect only complete types *)
| Some s -> s in
let off_accu = match cautious_mul e size with
- | None -> error "'offsetof' overflows"
+ | None -> fatal_error "'offsetof' overflows"
| Some s -> off_accu + s in
env,off_accu,sub_ty
- | ATINDEX_INIT _,_ -> error "subscripted value is not an array" in
+ | ATINDEX_INIT _,_ -> fatal_error "subscripted value is not an array" in
let env,offset,_ = List.fold_left offset_of_member (env,0,ty) mem in
let size_t = size_t_ikind () in
let offset = Ceval.normalize_int (Int64.of_int offset) size_t in
@@ -1715,46 +1939,46 @@ let elab_expr vararg loc env a =
| UNARY(PLUS, a1) ->
let b1,env = elab env a1 in
if not (is_arith_type env b1.etyp) then
- error "invalid argument type %a to unary '+'" (print_typ env) b1.etyp;
+ fatal_error "invalid argument type %a to unary '+'" (print_typ env) b1.etyp;
{ edesc = EUnop(Oplus, b1); etyp = unary_conversion env b1.etyp },env
| UNARY(MINUS, a1) ->
let b1,env = elab env a1 in
if not (is_arith_type env b1.etyp) then
- error "invalid argument type %a to unary '-'" (print_typ env) b1.etyp;
+ fatal_error "invalid argument type %a to unary '-'" (print_typ env) b1.etyp;
{ edesc = EUnop(Ominus, b1); etyp = unary_conversion env b1.etyp },env
| UNARY(BNOT, a1) ->
let b1,env = elab env a1 in
if not (is_integer_type env b1.etyp) then
- error "invalid argument type %a to unary '~'" (print_typ env) b1.etyp;
+ fatal_error "invalid argument type %a to unary '~'" (print_typ env) b1.etyp;
{ edesc = EUnop(Onot, b1); etyp = unary_conversion env b1.etyp },env
| UNARY(NOT, a1) ->
let b1,env = elab env a1 in
if not (is_scalar_type env b1.etyp) then
- error "invalid argument type %a to unary '!'" (print_typ env) b1.etyp;
+ fatal_error "invalid argument type %a to unary '!'" (print_typ env) b1.etyp;
{ edesc = EUnop(Olognot, b1); etyp = TInt(IInt, []) },env
| UNARY(ADDROF, a1) ->
let b1,env = elab env a1 in
if not (is_lvalue b1 || is_function_type env b1.etyp) then
- err "argument of '&' is not an lvalue (invalid %a)" (print_typ env) b1.etyp;
+ error "argument of '&' is not an lvalue (invalid %a)" (print_typ env) b1.etyp;
begin match b1.edesc with
| EVar id ->
begin match wrap Env.find_ident loc env id with
| Env.II_ident(Storage_register, _) ->
- err "address of register variable '%s' requested" id.C.name
+ error "address of register variable '%s' requested" id.C.name
| _ -> ()
end
| EUnop(Odot f, b2) ->
let fld = wrap2 field_of_dot_access loc env b2.etyp f in
if fld.fld_bitfield <> None then
- err "address of bit-field '%s' requested" f
+ error "address of bit-field '%s' requested" f
| EUnop(Oarrow f, b2) ->
let fld = wrap2 field_of_arrow_access loc env b2.etyp f in
if fld.fld_bitfield <> None then
- err "address of bit-field '%s' requested" f
+ error "address of bit-field '%s' requested" f
| _ -> ()
end;
{ edesc = EUnop(Oaddrof, b1); etyp = TPtr(b1.etyp, []) },env
@@ -1767,7 +1991,7 @@ let elab_expr vararg loc env a =
| TPtr(ty, _) | TArray(ty, _, _) ->
{ edesc = EUnop(Oderef, b1); etyp = ty },env
| _ ->
- error "arguemnt of unary '*' is not a pointer (%a invalid)"
+ fatal_error "argument of unary '*' is not a pointer (%a invalid)"
(print_typ env) b1.etyp
end
@@ -1798,7 +2022,7 @@ let elab_expr vararg loc env a =
match unroll env b1.etyp, unroll env b2.etyp with
| (TPtr(ty, a) | TArray(ty, _, a)), (TInt _ | TEnum _) -> ty
| (TInt _ | TEnum _), (TPtr(ty, a) | TArray(ty, _, a)) -> ty
- | _, _ -> error "invalid operands to binary '+' (%a and %a)"
+ | _, _ -> fatal_error "invalid operands to binary '+' (%a and %a)"
(print_typ env) b1.etyp (print_typ env) b2.etyp
in
check_ptr_arith env ty '+';
@@ -1817,21 +2041,19 @@ let elab_expr vararg loc env a =
match wrap unroll loc env b1.etyp, wrap unroll loc env b2.etyp with
| (TPtr(ty, a) | TArray(ty, _, a)), (TInt _ | TEnum _) ->
if not (wrap pointer_arithmetic_ok loc env ty) then
- err "illegal pointer arithmetic in binary '-'";
- (TPtr(ty, []), TPtr(ty, []))
- | (TInt _ | TEnum _), (TPtr(ty, a) | TArray(ty, _, a)) ->
- check_ptr_arith env ty '-';
+ error "illegal pointer arithmetic in binary '-'";
(TPtr(ty, []), TPtr(ty, []))
| (TPtr(ty1, a1) | TArray(ty1, _, a1)),
(TPtr(ty2, a2) | TArray(ty2, _, a2)) ->
if not (compatible_types AttrIgnoreAll env ty1 ty2) then
- err "%a and %a are not pointers to compatible types"
+ error "%a and %a are not pointers to compatible types"
(print_typ env) b1.etyp (print_typ env) b1.etyp;
check_ptr_arith env ty1 '-';
+ check_ptr_arith env ty2 '-';
if wrap sizeof loc env ty1 = Some 0 then
- err "subtraction between two pointers to zero-sized objects";
+ error "subtraction between two pointers to zero-sized objects";
(TPtr(ty1, []), TInt(ptrdiff_t_ikind(), []))
- | _, _ -> error "invalid operands to binary '-' (%a and %a)"
+ | _, _ -> fatal_error "invalid operands to binary '-' (%a and %a)"
(print_typ env) b1.etyp (print_typ env) b2.etyp
end in
{ edesc = EBinop(Osub, b1, b2, tyop); etyp = tyres },env
@@ -1875,7 +2097,7 @@ let elab_expr vararg loc env a =
let b2,env = elab env a2 in
let b3,env = elab env a3 in
if not (is_scalar_type env b1.etyp) then
- err "first argument of '?:' is not a scalar type (invalid %a)"
+ error "first argument of '?:' is not a scalar type (invalid %a)"
(print_typ env) b1.etyp;
begin match pointer_decay env b2.etyp, pointer_decay env b3.etyp with
| (TInt _ | TFloat _ | TEnum _), (TInt _ | TFloat _ | TEnum _) ->
@@ -1903,7 +2125,7 @@ let elab_expr vararg loc env a =
| ty1, ty2 ->
match combine_types AttrIgnoreAll env ty1 ty2 with
| None ->
- error "the second and third argument of '?:' incompatible types (%a and %a)"
+ fatal_error "the second and third argument of '?:' incompatible types (%a and %a)"
(print_typ env) ty1 (print_typ env) ty2
| Some tyres ->
{ edesc = EConditional(b1, b2, b3); etyp = tyres },env
@@ -1917,7 +2139,7 @@ let elab_expr vararg loc env a =
if List.mem AConst (attributes_of_type env b1.etyp) then
error "left-hand side of assignment has 'const' type";
if not (is_modifiable_lvalue env b1) then
- err "expression is not assignable";
+ error "expression is not assignable";
if not (wrap2 valid_assignment loc env b2 b1.etyp) then begin
if wrap2 valid_cast loc env b2.etyp b1.etyp then
if wrap2 int_pointer_conversion loc env b2.etyp b1.etyp then
@@ -1926,10 +2148,10 @@ let elab_expr vararg loc env a =
(print_typ env) b1.etyp (print_typ env) b2.etyp
else
warning Unnamed
- "incompatible conversion assigning to %a from %a"
+ "incompatible conversion: assigning to %a from %a"
(print_typ env) b1.etyp (print_typ env) b2.etyp
else
- err "assigning to %a from incompatible type %a"
+ error "assigning to %a from incompatible type %a"
(print_typ env) b1.etyp (print_typ env) b2.etyp;
end;
{ edesc = EBinop(Oassign, b1, b2, b1.etyp); etyp = b1.etyp },env
@@ -1953,9 +2175,9 @@ let elab_expr vararg loc env a =
begin match elab env (BINARY(sop, a1, a2)) with
| ({ edesc = EBinop(_, b1, b2, _); etyp = ty } as b),env ->
if List.mem AConst (attributes_of_type env b1.etyp) then
- err "left-hand side of assignment has 'const' type";
+ error "left-hand side of assignment has 'const' type";
if not (is_modifiable_lvalue env b1) then
- err "expression is not assignable";
+ error "expression is not assignable";
if not (wrap2 valid_assignment loc env b b1.etyp) then begin
if wrap2 valid_cast loc env ty b1.etyp then
if wrap2 int_pointer_conversion loc env ty b1.etyp then
@@ -1964,10 +2186,10 @@ let elab_expr vararg loc env a =
(print_typ env) b1.etyp (print_typ env) ty
else
warning Unnamed
- "incompatible conversion assigning to %a from %a"
+ "incompatible conversion: assigning to %a from %a"
(print_typ env) b1.etyp (print_typ env) ty
else
- err "assigning to %a from incompatible type %a"
+ error "assigning to %a from incompatible type %a"
(print_typ env) b1.etyp (print_typ env) ty;
end;
{ edesc = EBinop(top, b1, b2, ty); etyp = b1.etyp },env
@@ -1986,9 +2208,9 @@ let elab_expr vararg loc env a =
and elab_pre_post_incr_decr op msg a1 =
let b1,env = elab env a1 in
if not (is_modifiable_lvalue env b1) then
- err "expression is not assignable";
+ error "expression is not assignable";
if not (is_scalar_type env b1.etyp) then
- err "cannot %s value of type %a" msg (print_typ env) b1.etyp;
+ error "cannot %s value of type %a" msg (print_typ env) b1.etyp;
{ edesc = EUnop(op, b1); etyp = b1.etyp },env
(* Elaboration of binary operators over integers *)
@@ -1996,7 +2218,7 @@ let elab_expr vararg loc env a =
let b1,env = elab env a1 in
let b2,env = elab env a2 in
if not ((is_integer_type env b1.etyp) && (is_integer_type env b2.etyp)) then
- error "invalid operands to binary '%s' (%a and %a)" msg
+ fatal_error "invalid operands to binary '%s' (%a and %a)" msg
(print_typ env) b1.etyp (print_typ env) b2.etyp;
let tyres = binary_conversion env b1.etyp b2.etyp in
{ edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env
@@ -2006,7 +2228,7 @@ let elab_expr vararg loc env a =
let b1,env = elab env a1 in
let b2,env = elab env a2 in
if not ((is_arith_type env b1.etyp) && (is_arith_type env b2.etyp)) then
- error "invalid operands to binary '%s' (%a and %a)" msg
+ fatal_error "invalid operands to binary '%s' (%a and %a)" msg
(print_typ env) b1.etyp (print_typ env) b2.etyp;
let tyres = binary_conversion env b1.etyp b2.etyp in
{ edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env
@@ -2016,7 +2238,7 @@ let elab_expr vararg loc env a =
let b1,env = elab env a1 in
let b2,env = elab env a2 in
if not ((is_integer_type env b1.etyp) && (is_integer_type env b2.etyp)) then
- error "invalid operands to '%s' (%a and %a)" msg
+ fatal_error "invalid operands to '%s' (%a and %a)" msg
(print_typ env) b1.etyp (print_typ env) b2.etyp;
let tyres = unary_conversion env b1.etyp in
{ edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env
@@ -2043,6 +2265,10 @@ let elab_expr vararg loc env a =
if not (compatible_types AttrIgnoreAll env ty1 ty2) then
warning Compare_distinct_pointer_types "comparison of distinct pointer types (%a and %a)"
(print_typ env) b1.etyp (print_typ env) b2.etyp;
+ let incomp_ty1 = wrap incomplete_type loc env ty1
+ and incomp_ty2 = wrap incomplete_type loc env ty2 in
+ if incomp_ty1 <> incomp_ty2 then
+ warning Unnamed "comparison of complete and incomplete pointers";
EBinop(op, b1, b2, TPtr(ty1, []))
| TPtr _, (TInt _ | TEnum _)
| (TInt _ | TEnum _), TPtr _ ->
@@ -2050,7 +2276,7 @@ let elab_expr vararg loc env a =
(print_typ env) b1.etyp (print_typ env) b2.etyp;
EBinop(op, b1, b2, TPtr(TVoid [], []))
| ty1, ty2 ->
- error "illegal comparison between types@ %a@ and %a"
+ fatal_error "illegal comparison between types@ %a@ and %a"
(print_typ env) b1.etyp (print_typ env) b2.etyp; in
{ edesc = resdesc; etyp = TInt(IInt, []) },env
@@ -2059,7 +2285,7 @@ let elab_expr vararg loc env a =
let b1,env = elab env a1 in
let b2,env = elab env a2 in
if not ((is_scalar_type env b1.etyp) && (is_scalar_type env b2.etyp)) then
- error "invalid operands to binary %s (%a and %a)" msg
+ fatal_error "invalid operands to binary '%s' (%a and %a)" msg
(print_typ env) b1.etyp (print_typ env) b2.etyp;
{ edesc = EBinop(op, b1, b2, TInt(IInt, [])); etyp = TInt(IInt, []) },env
@@ -2071,14 +2297,14 @@ let elab_expr vararg loc env a =
let found = argno - 1 in
let expected = List.length params + found in
let vararg = if vararg then "at least " else "" in
- err "too few arguments to function call, expected %s%d, have %d" vararg expected found; [],env
+ error "too few arguments to function call, expected %s%d, have %d" vararg expected found; [],env
| (_::_,env), [] ->
if vararg
then args
else
let expected = argno - 1 in
let found = List.length (fst args) + expected in
- (err "too many arguments to function call, expected %d, have %d" expected found; args)
+ (error "too many arguments to function call, expected %d, have %d" expected found; args)
| (arg1 :: argl,env), (_, ty_p) :: paraml ->
let ty_a = argument_conversion env arg1.etyp in
if not (wrap2 valid_assignment loc env {arg1 with etyp = ty_a} ty_p)
@@ -2090,10 +2316,10 @@ let elab_expr vararg loc env a =
(print_typ env) ty_a argno (print_typ env) ty_p
else
warning Unnamed
- "incompatible conversion passing %a to parameter %d of type %a"
+ "incompatible conversion: passing %a to parameter %d of type %a"
(print_typ env) ty_a argno (print_typ env) ty_p end
else
- err
+ error
"passing %a to parameter %d of incompatible type %a"
(print_typ env) ty_a argno (print_typ env) ty_p
end;
@@ -2102,16 +2328,16 @@ let elab_expr vararg loc env a =
in elab env a
(* Filling in forward declaration *)
-let _ = elab_expr_f := (elab_expr false)
+let _ = elab_expr_f := (elab_expr ctx_constexp)
-let elab_opt_expr vararg loc env = function
+let elab_opt_expr ctx loc env = function
| None -> None,env
- | Some a -> let a,env = (elab_expr vararg loc env a) in
+ | Some a -> let a,env = (elab_expr ctx loc env a) in
Some a,env
-let elab_for_expr vararg loc env = function
+let elab_for_expr ctx loc env = function
| None -> { sdesc = Sskip; sloc = elab_loc loc },env
- | Some a -> let a,env = elab_expr vararg loc env a in
+ | Some a -> let a,env = elab_expr ctx loc env a in
{ sdesc = Sdo a; sloc = elab_loc loc },env
(* Handling of __func__ (section 6.4.2.2) *)
@@ -2125,49 +2351,69 @@ let __func__type_and_init s =
let enter_typedefs loc env sto dl =
if sto <> Storage_default then
- error loc "non-default storage-class on 'typedef' definition";
+ error loc "non-default storage class on 'typedef' definition";
+ if dl = [] then
+ warning loc Missing_declarations "typedef requires a name";
List.fold_left (fun env (s, ty, init) ->
if init <> NO_INIT then
error loc "initializer in typedef";
+ if has_std_alignas env ty then
+ error loc "alignment specified for typedef '%s'" s;
match previous_def Env.lookup_typedef env s with
- | Some (s',ty') ->
+ | Some (s',ty') when Env.in_current_scope env s' ->
if equal_types env ty ty' then begin
- warning loc Celeven_extension "redefinition of typedef '%s' is C11 extension" s;
+ warning loc Celeven_extension "redefinition of typedef '%s' is a C11 extension" s;
env
end else begin
error loc "typedef redefinition with different types (%a vs %a)"
(print_typ env) ty (print_typ env) ty';
env
end
- | None ->
+ | _ ->
if redef Env.lookup_ident env s then
error loc "redefinition of '%s' as different kind of symbol" s;
let (id, env') = Env.enter_typedef env s ty in
+ check_reduced_alignment loc env' ty;
emit_elab env loc (Gtypedef(id, ty));
env') env dl
-let enter_decdefs local loc env sto dl =
+let enter_decdefs local nonstatic_inline loc env sto dl =
(* Sanity checks on storage class *)
- if sto = Storage_register && not local then
- fatal_error loc "'register' storage-class on file-scoped variable";
+ if (sto = Storage_auto || sto = Storage_register) && not local then
+ fatal_error loc "illegal storage class %s on file-scoped variable"
+ (name_of_storage_class sto);
if sto <> Storage_default && dl = [] then
warning loc Missing_declarations "declaration does not declare anything";
let enter_decdef (decls, env) (s, ty, init) =
let isfun = is_function_type env ty in
+ if sto = Storage_register && has_std_alignas env ty then
+ error loc "alignment specified for 'register' object '%s'" s;
if sto = Storage_extern && init <> NO_INIT then
- error loc "'extern' declaration variable has an initializer";
+ error loc "'extern' declaration variable has an initializer";
if local && isfun then begin
match sto with
- | Storage_static -> error loc "function declared in block scope cannot have 'static' storage-class";
- | Storage_register -> error loc "invalid 'register' storage-class on function";
+ | Storage_static ->
+ error loc "function declared in block scope cannot have 'static' storage class"
+ | Storage_auto | Storage_register ->
+ error loc "illegal storage class %s on function"
+ (name_of_storage_class sto)
| _ -> ()
end;
- (* Local function declarations are always treated as extern *)
- let sto1 = if local && isfun then Storage_extern else sto in
+ if is_qualified_array ty then
+ error loc "type qualifier used in array declarator outside of function prototype";
+ (* Local variable declarations with default storage are treated as 'auto'.
+ Local function declarations with default storage remain with
+ default storage. *)
+ let sto1 =
+ if local && sto = Storage_default && not isfun
+ then Storage_auto
+ else sto in
(* enter ident in environment with declared type, because
initializer can refer to the ident *)
let (id, sto', env1, ty, linkage) =
enter_or_refine_ident local loc env s sto1 ty in
+ if init <> NO_INIT && not local then
+ add_global_define loc s;
if not isfun && is_void_type env ty then
fatal_error loc "'%s' has incomplete type" s;
(* process the initializer *)
@@ -2175,16 +2421,30 @@ let enter_decdefs local loc env sto dl =
(* update environment with refined type *)
let env2 = Env.add_ident env1 id sto' ty' in
(* check for incomplete type *)
- if local && sto' <> Storage_extern
- && not isfun
- && wrap incomplete_type loc env ty' then
- error loc "variable has incomplete type %a" (print_typ env) ty';
+ if not isfun && wrap incomplete_type loc env ty' then
+ if not local && sto' = Storage_static then begin
+ warning loc Tentative_incomplete_static "tentative static definition with incomplete type";
+ end else if local && sto' <> Storage_extern then
+ error loc "variable has incomplete type %a" (print_typ env) ty';
+ (* check if alignment is reduced *)
+ check_reduced_alignment loc env ty';
+ (* check for static variables in nonstatic inline functions *)
+ if local && nonstatic_inline
+ && sto' = Storage_static
+ && not (List.mem AConst (attributes_of_type env ty')) then
+ warning loc Static_in_inline "non-constant static local variable '%s' in inline function may be different in different files" s;
if local && not isfun && sto' <> Storage_extern && sto' <> Storage_static then
(* Local definition *)
((sto', id, ty', init') :: decls, env2)
else begin
(* Global definition *)
emit_elab ~linkage env2 loc (Gdecl(sto', id, ty', init'));
+ (* Make sure the initializer is constant. *)
+ begin match init' with
+ | Some i when not (Ceval.is_constant_init env2 i) ->
+ error loc "initializer is not a compile-time constant"
+ | _ -> ()
+ end;
(decls, env2)
end in
let (decls, env') = List.fold_left enter_decdef ([], env) dl in
@@ -2216,7 +2476,7 @@ let elab_KR_function_parameters env params defs loc =
(* Check that the declarations only declare parameters *)
let extract_name (Init_name(Name(s, dty, attrs, loc') as name, ie)) =
if not (List.mem s params) then
- error loc' "Declaration of '%s' which is not a function parameter" s;
+ error loc' "declaration of '%s' which is not a function parameter" s;
if ie <> NO_INIT then
error loc' "illegal initialization of parameter '%s'" s;
name
@@ -2225,18 +2485,17 @@ let elab_KR_function_parameters env params defs loc =
let elab_param_def env = function
| DECDEF((spec', name_init_list), loc') ->
let name_list = List.map extract_name name_init_list in
- let (paramsenv, sto) = elab_name_group true loc' env (spec', name_list) in
- begin match sto with
- | Storage_extern ->
- error loc' "invalid 'extern' storage-class specifier for function parameter"
- | Storage_static ->
- error loc' "invalid 'static' storage-class specifier for function parameter"
- | _ -> ()
- end;
+ if name_list = [] then
+ error loc' "declaration does not declare a parameter";
+ let (paramsenv, sto) = elab_name_group loc' env (spec', name_list) in
+ if sto <> Storage_default && sto <> Storage_register then
+ error loc' (* NB: 'auto' not allowed *)
+ "invalid storage class %s for function parameter"
+ (name_of_storage_class sto);
paramsenv
| d -> (* Should never be produced by the parser *)
fatal_error (Cabshelper.get_definitionloc d)
- "Illegal declaration of function parameter" in
+ "illegal declaration of function parameter" in
let kr_params_defs,paramsenv =
let params,paramsenv = mmap elab_param_def env defs in
List.concat params,paramsenv in
@@ -2296,11 +2555,22 @@ let inherit_vararg env s sto ty =
(* Function definitions *)
-let elab_fundef env spec name defs body loc =
- let (s, sto, inline, noret, ty, kr_params, env1) =
- elab_fundef_name true env spec name in
- if sto = Storage_register then
- fatal_error loc "invalid 'register' storage-class on function";
+let elab_fundef genv spec name defs body loc =
+ (* We maintain two environments:
+ - genv is the "global", file-scope environment. It is enriched
+ with the declaration of the function, and also with
+ structs and unions defined as part of its return type.
+ - lenv is the "local" environment used to elaborate the function body.
+ It contains everything that genv contains, including
+ a declaration for the function, so as to support recursive calls.
+ In addition, it contains declarations for function parameters
+ and structs and unions defined in the parameter list. *)
+ let (fun_id, sto, inline, noret, ty, kr_params, genv, lenv) =
+ elab_fundef_name genv spec name in
+ let s = fun_id.C.name in
+ if sto = Storage_auto || sto = Storage_register then
+ fatal_error loc "invalid storage class %s on function"
+ (name_of_storage_class sto);
begin match kr_params, defs with
| None, d::_ ->
error (Cabshelper.get_definitionloc d)
@@ -2310,54 +2580,77 @@ let elab_fundef env spec name defs body loc =
(* Process the parameters and the K&R declarations, if any, to obtain:
- [ty]: the full, prototyped type of the function
- [extra_decls]: extra declarations to be inserted at the
- beginning of the function *)
- let (ty, extra_decls,env1) =
+ beginning of the function
+ - [lenv]: a first cut at the local environment, containing in particular
+ the structs and unions defined in the parameter list. *)
+ let (ty, extra_decls, lenv) =
match ty, kr_params with
| TFun(ty_ret, None, vararg, attr), None ->
- (TFun(ty_ret, Some [], vararg, attr), [],env1)
+ (TFun(ty_ret, Some [], vararg, attr), [], lenv)
| ty, None ->
- (ty, [],env1)
+ (ty, [], lenv)
| TFun(ty_ret, None, false, attr), Some params ->
warning loc CompCert_conformance "non-prototype, pre-standard function definition, converting to prototype form";
- let (params', extra_decls,env) =
- elab_KR_function_parameters env params defs loc in
- (TFun(ty_ret, Some params', inherit_vararg env s sto ty, attr), extra_decls,env)
+ let (params', extra_decls, lenv) =
+ elab_KR_function_parameters lenv params defs loc in
+ (TFun(ty_ret, Some params', inherit_vararg genv s sto ty, attr), extra_decls, lenv)
| _, Some params ->
assert false
in
- (* Extract infos from the type of the function *)
+ (* Extract infos from the type of the function.
+ Checks on the return type must be done in the global environment. *)
let (ty_ret, params, vararg, attr) =
match ty with
| TFun(ty_ret, Some params, vararg, attr) ->
- if wrap incomplete_type loc env1 ty_ret && not (is_void_type env ty_ret) then
+ if has_std_alignas genv ty then
+ error loc "alignment specified for function '%s'" s;
+ if wrap incomplete_type loc genv ty_ret && not (is_void_type genv ty_ret) then
fatal_error loc "incomplete result type %a in function definition"
- (print_typ env) ty_ret;
+ (print_typ genv) ty_ret;
(ty_ret, params, vararg, attr)
| _ -> fatal_error loc "wrong type for function definition" in
- (* Enter function in the environment, for recursive references *)
- let (fun_id, sto1, env1, _, _) =
- enter_or_refine_ident false loc env1 s sto ty in
- let incomplete_param env ty =
+ (* Enter function in the global environment *)
+ let (fun_id, sto1, genv, new_ty, _) =
+ enter_or_refine_function loc genv fun_id sto ty in
+ add_global_define loc s;
+ (* Also enter it in the local environment, for recursive references *)
+ let lenv = Env.add_ident lenv fun_id sto new_ty in
+ (* Take into account attributes from previous declarations of the function *)
+ let attr = attributes_of_type genv new_ty in
+ (* Additional checks on function parameters. They should have a complete type
+ and additionally they should have an identifier. In both cases a fatal
+ error is raised in order to avoid problems at later places. *)
+ let add_param env (id, ty) =
if wrap incomplete_type loc env ty then
- fatal_error loc "parameter has incomplete type" in
- (* Enter parameters and extra declarations in the environment *)
- let env2 =
- List.fold_left (fun e (id, ty) -> incomplete_param e ty;
- Env.add_ident e id Storage_default ty)
- (Env.new_scope env1) params in
- let env2 =
+ fatal_error loc "parameter has incomplete type";
+ if id.C.name = "" then
+ fatal_error loc "parameter name omitted";
+ Env.add_ident env id Storage_default ty
+ in
+ (* Enter parameters and extra declarations in the local environment.
+ For K&R functions this hasn't been done yet.
+ For prototyped functions this has been done by [elab_fundef_name]
+ already, but some parameter may have been shadowed by the
+ function name, while it should be the other way around, e.g.
+ [int f(int f) { return f+1; }], with [f] refering to the
+ parameter [f] and not to the function [f] within the body of the
+ function. *)
+ let lenv =
+ List.fold_left add_param lenv params in
+ let lenv =
List.fold_left (fun e (sto, id, ty, init) -> Env.add_ident e id sto ty)
- env2 extra_decls in
- (* Define "__func__" and enter it in the environment *)
+ lenv extra_decls in
+ (* Define "__func__" and enter it in the local environment *)
let (func_ty, func_init) = __func__type_and_init s in
- let (func_id, _, env3, func_ty, _) =
- enter_or_refine_ident true loc env2 "__func__" Storage_static func_ty in
- emit_elab ~debuginfo:false env3 loc
+ let (func_id, _, lenv, func_ty, _) =
+ enter_or_refine_ident true loc lenv "__func__" Storage_static func_ty in
+ emit_elab ~debuginfo:false lenv loc
(Gdecl(Storage_static, func_id, func_ty, Some func_init));
(* Elaborate function body *)
- let body1 = !elab_funbody_f ty_ret vararg env3 body in
+ let body1 = !elab_funbody_f ty_ret vararg (inline && sto <> Storage_static)
+ lenv body in
(* Analyse it for returns *)
- let (can_return, can_fallthrough) = Cflow.function_returns env3 body1 in
+ let (can_return, can_fallthrough) = Cflow.function_returns lenv body1 in
(* Special treatment of the "main" function. *)
let body2 =
if s = "main" then begin
@@ -2365,7 +2658,7 @@ let elab_fundef env spec name defs body loc =
error loc "'main' is not allowed to be declared inline";
if noret then
warning loc Unnamed "'main' is not allowed to be declared _Noreturn";
- match unroll env ty_ret with
+ match unroll genv ty_ret with
| TInt(IInt, []) ->
(* Add implicit "return 0;" at end of function body.
If we trusted the return analysis, we would do this only if
@@ -2378,7 +2671,7 @@ let elab_fundef env spec name defs body loc =
end else begin
(* For non-"main" functions, warn if the body can fall through
and the return type is not "void". *)
- if can_fallthrough && not (is_void_type env ty_ret) then
+ if can_fallthrough && not (is_void_type genv ty_ret) then
warning loc Return_type "control reaches end of non-void function";
body1
end in
@@ -2407,12 +2700,13 @@ let elab_fundef env spec name defs body loc =
fd_vararg = vararg;
fd_locals = [];
fd_body = body3 } in
- emit_elab ~linkage:true env1 loc (Gfundef fn);
- env1
+ emit_elab ~linkage:true genv loc (Gfundef fn);
+ genv
(* Definitions *)
-let rec elab_definition (local: bool) (env: Env.t) (def: Cabs.definition)
+let elab_definition (for_loop: bool) (local: bool) (nonstatic_inline: bool)
+ (env: Env.t) (def: Cabs.definition)
: decl list * Env.t =
match def with
(* "int f(int x) { ... }" *)
@@ -2425,44 +2719,32 @@ let rec elab_definition (local: bool) (env: Env.t) (def: Cabs.definition)
(* "int x = 12, y[10], *z" *)
| DECDEF(init_name_group, loc) ->
let ((dl, env1), sto, tydef) =
- elab_init_name_group false loc env init_name_group in
+ elab_init_name_group loc env init_name_group in
+ if for_loop then begin
+ let fun_declaration = List.exists (fun (_, ty, _) -> is_function_type env ty) dl in
+ if fun_declaration || sto = Storage_extern || sto = Storage_static || tydef then
+ error loc "declaration of non-local variable in 'for' loop" ;
+ end;
if tydef then
let env2 = enter_typedefs loc env1 sto dl
in ([], env2)
else
- enter_decdefs local loc env1 sto dl
+ enter_decdefs local nonstatic_inline loc env1 sto dl
(* pragma *)
| PRAGMA(s, loc) ->
emit_elab env loc (Gpragma s);
([], env)
-and elab_definitions local env = function
- | [] -> ([], env)
- | d1 :: dl ->
- let (decl1, env1) = elab_definition local env d1 in
- let (decl2, env2) = elab_definitions local env1 dl in
- (decl1 @ decl2, env2)
-
(* Extended asm *)
-let elab_asm_operand vararg loc env (ASMOPERAND(label, wide, chars, e)) =
+let elab_asm_operand ctx loc env (ASMOPERAND(label, wide, chars, e)) =
let s = elab_simple_string loc wide chars in
- let e',env = elab_expr vararg loc env e in
+ let e',env = elab_expr ctx loc env e in
(label, s, e'),env
-(* Contexts for elaborating statements *)
-
-module StringSet = Set.Make(String)
-
-type stmt_context = {
- ctx_return_typ: typ; (**r return type for the function *)
- ctx_labels: StringSet.t; (**r all labels defined in the function *)
- ctx_break: bool; (**r is 'break' allowed? *)
- ctx_continue: bool; (**r is 'continue' allowed? *)
- ctx_vararg: bool; (**r is this a vararg function? *)
-}
+(* Operations over contexts *)
let stmt_labels stmt =
let lbls = ref StringSet.empty in
@@ -2487,7 +2769,48 @@ let stmt_labels stmt =
let ctx_loop ctx = { ctx with ctx_break = true; ctx_continue = true }
-let ctx_switch ctx = { ctx with ctx_break = true }
+let ctx_switch ctx = { ctx with ctx_break = true; ctx_in_switch = true }
+
+(* Check the uniqueness of 'case' and 'default' in a 'switch' *)
+
+module Int64Set = Set.Make(Int64)
+
+let check_switch_cases switch_body =
+ let cases = ref Int64Set.empty
+ and default = ref false in
+ let rec check s =
+ match s.sdesc with
+ | Sskip -> ()
+ | Sdo _ -> ()
+ | Sseq(s1, s2) -> check s1; check s2
+ | Sif(_, s1, s2) -> check s1; check s2
+ | Swhile(_, s1) -> check s1
+ | Sdowhile(s1, _) -> check s1
+ | Sfor(s1, _, s2, s3) -> check s1; check s2; check s3
+ | Sbreak -> ()
+ | Scontinue -> ()
+ | Sswitch(_, _) -> () (* already checked during elaboration of this switch *)
+ | Slabeled(lbl, s1) ->
+ begin match lbl with
+ | Slabel _ -> ()
+ | Scase(_, n) ->
+ if Int64Set.mem n !cases then
+ Diagnostics.error s.sloc "duplicate case value '%Ld'" n
+ else
+ cases := Int64Set.add n !cases
+ | Sdefault ->
+ if !default then
+ Diagnostics.error s.sloc "multiple default labels in one switch"
+ else
+ default := true
+ end;
+ check s1
+ | Sgoto _ -> ()
+ | Sreturn _ -> ()
+ | Sblock sl -> List.iter check sl
+ | Sdecl _ -> ()
+ | Sasm _ -> ()
+ in check switch_body
(* Elaboration of statements *)
@@ -2498,7 +2821,7 @@ let rec elab_stmt env ctx s =
(* 6.8.3 Expression statements *)
| COMPUTATION(a, loc) ->
- let a,env = (elab_expr ctx.ctx_vararg loc env a) in
+ let a,env = elab_expr ctx loc env a in
{ sdesc = Sdo a; sloc = elab_loc loc },env
(* 6.8.1 Labeled statements *)
@@ -2508,16 +2831,20 @@ let rec elab_stmt env ctx s =
{ sdesc = Slabeled(Slabel lbl, s1); sloc = elab_loc loc },env
| CASE(a, s1, loc) ->
- let a',env = elab_expr ctx.ctx_vararg loc env a in
- begin match Ceval.integer_expr env a' with
+ if not ctx.ctx_in_switch then
+ error loc "'case' statement not in switch statement";
+ let a',env = elab_expr ctx loc env a in
+ let n =
+ match Ceval.integer_expr env a' with
| None ->
- error loc "expression of 'case' label is not an integer constant expression"
- | Some n -> ()
- end;
+ error loc "expression of 'case' label is not an integer constant expression"; 0L
+ | Some n -> n in
let s1,env = elab_stmt env ctx s1 in
- { sdesc = Slabeled(Scase a', s1); sloc = elab_loc loc },env
+ { sdesc = Slabeled(Scase(a', n), s1); sloc = elab_loc loc },env
| DEFAULT(s1, loc) ->
+ if not ctx.ctx_in_switch then
+ error loc "'case' statement not in switch statement";
let s1,env = elab_stmt env ctx s1 in
{ sdesc = Slabeled(Sdefault, s1); sloc = elab_loc loc },env
@@ -2529,7 +2856,7 @@ let rec elab_stmt env ctx s =
(* 6.8.4 Conditional statements *)
| If(a, s1, s2, loc) ->
- let a',env = elab_expr ctx.ctx_vararg loc env a in
+ let a',env = elab_expr ctx loc env a in
if not (is_scalar_type env a'.etyp) then
error loc "controlling expression of 'if' does not have scalar type (%a invalid)"
(print_typ env) a'.etyp;
@@ -2544,7 +2871,7 @@ let rec elab_stmt env ctx s =
(* 6.8.5 Iterative statements *)
| WHILE(a, s1, loc) ->
- let a',env = elab_expr ctx.ctx_vararg loc env a in
+ let a',env = elab_expr ctx loc env a in
if not (is_scalar_type env a'.etyp) then
error loc "controlling expression of 'while' does not have scalar type (%a invalid)"
(print_typ env) a'.etyp;
@@ -2553,7 +2880,7 @@ let rec elab_stmt env ctx s =
| DOWHILE(a, s1, loc) ->
let s1',env = elab_stmt env (ctx_loop ctx) s1 in
- let a',env = elab_expr ctx.ctx_vararg loc env a in
+ let a',env = elab_expr ctx loc env a in
if not (is_scalar_type env a'.etyp) then
error loc "controlling expression of 'while' does not have scalar type (%a invalid)"
(print_typ env) a'.etyp;
@@ -2563,24 +2890,25 @@ let rec elab_stmt env ctx s =
let (a1', env_decls, decls') =
match fc with
| Some (FC_EXP a1) ->
- let a1,env = elab_for_expr ctx.ctx_vararg loc env (Some a1) in
+ let a1,env = elab_for_expr ctx loc env (Some a1) in
(a1, env, None)
| None ->
- let a1,env = elab_for_expr ctx.ctx_vararg loc env None in
+ let a1,env = elab_for_expr ctx loc env None in
(a1, env, None)
| Some (FC_DECL def) ->
- let (dcl, env') = elab_definition true (Env.new_scope env) def in
+ let (dcl, env') = elab_definition true true ctx.ctx_nonstatic_inline
+ (Env.new_scope env) def in
let loc = elab_loc (Cabshelper.get_definitionloc def) in
(sskip, env',
Some(List.map (fun d -> {sdesc = Sdecl d; sloc = loc}) dcl)) in
let a2',env_test =
match a2 with
| None -> intconst 1L IInt,env_decls
- | Some a2 -> elab_expr ctx.ctx_vararg loc env_decls a2
+ | Some a2 -> elab_expr ctx loc env_decls a2
in
if not (is_scalar_type env_test a2'.etyp) then
error loc "controlling expression of 'for' does not have scalar type (%a invalid)" (print_typ env) a2'.etyp;
- let a3',env_for = elab_for_expr ctx.ctx_vararg loc env_test a3 in
+ let a3',env_for = elab_for_expr ctx loc env_test a3 in
let s1',env_body = elab_stmt env_for (ctx_loop ctx) s1 in
let sfor = { sdesc = Sfor(a1', a2', a3', s1'); sloc = elab_loc loc } in
begin match decls' with
@@ -2590,11 +2918,12 @@ let rec elab_stmt env ctx s =
(* 6.8.4 Switch statement *)
| SWITCH(a, s1, loc) ->
- let a',env = elab_expr ctx.ctx_vararg loc env a in
+ let a',env = elab_expr ctx loc env a in
if not (is_integer_type env a'.etyp) then
error loc "controlling expression of 'switch' does not have integer type (%a invalid)"
(print_typ env) a'.etyp;
let s1',env = elab_stmt env (ctx_switch ctx) s1 in
+ check_switch_cases s1';
{ sdesc = Sswitch(a', s1'); sloc = elab_loc loc },env
(* 6.8.6 Break and continue statements *)
@@ -2609,7 +2938,7 @@ let rec elab_stmt env ctx s =
(* 6.8.6 Return statements *)
| RETURN(a, loc) ->
- let a',env = elab_opt_expr ctx.ctx_vararg loc env a in
+ let a',env = elab_opt_expr ctx loc env a in
begin match (unroll env ctx.ctx_return_typ, a') with
| TVoid _, None -> ()
| TVoid _, Some _ ->
@@ -2628,7 +2957,7 @@ let rec elab_stmt env ctx s =
(print_typ env) b.etyp (print_typ env) ctx.ctx_return_typ
else
warning loc Unnamed
- "incompatible conversion returning %a from a function with result type %a"
+ "incompatible conversion: returning %a from a function with result type %a"
(print_typ env) b.etyp (print_typ env) ctx.ctx_return_typ
else
error loc
@@ -2652,8 +2981,13 @@ let rec elab_stmt env ctx s =
| ASM(cv_specs, wide, chars, outputs, inputs, flags, loc) ->
let a = elab_cvspecs env cv_specs in
let s = elab_simple_string loc wide chars in
- let outputs,env = mmap (elab_asm_operand ctx.ctx_vararg loc) env outputs in
- let inputs ,env= mmap (elab_asm_operand ctx.ctx_vararg loc) env inputs in
+ let outputs,env = mmap (elab_asm_operand ctx loc) env outputs in
+ List.iter
+ (fun (lbl, cst, e) ->
+ if not (is_modifiable_lvalue env e) then
+ error loc "asm output is not a modifiable l-value";)
+ outputs;
+ let inputs ,env= mmap (elab_asm_operand ctx loc) env inputs in
let flags = List.map (fun (w,c) -> elab_simple_string loc w c) flags in
{ sdesc = Sasm(a, s, outputs, inputs, flags);
sloc = elab_loc loc },env
@@ -2672,7 +3006,8 @@ and elab_block_body env ctx sl =
| [] ->
[],env
| DEFINITION def :: sl1 ->
- let (dcl, env') = elab_definition true env def in
+ let (dcl, env') =
+ elab_definition false true ctx.ctx_nonstatic_inline env def in
let loc = elab_loc (Cabshelper.get_definitionloc def) in
let dcl = List.map (fun ((sto,id,ty,_) as d) ->
Debug.insert_local_declaration sto id ty loc;
@@ -2686,14 +3021,24 @@ and elab_block_body env ctx sl =
(* Elaboration of a function body. Return the corresponding C statement. *)
-let elab_funbody return_typ vararg env b =
+let elab_funbody return_typ vararg nonstatic_inline env b =
let ctx =
{ ctx_return_typ = return_typ;
ctx_labels = stmt_labels b;
ctx_break = false;
ctx_continue = false;
- ctx_vararg = vararg;} in
- fst(elab_stmt env ctx b)
+ ctx_in_switch = false;
+ ctx_vararg = vararg;
+ ctx_nonstatic_inline = nonstatic_inline } in
+ (* The function body appears as a block in the AST but should not create
+ a new scope. Instead, the scope used for function parameters must be
+ used for the body. *)
+ match b with
+ | BLOCK (b,loc) ->
+ let b',_ = elab_block_body env ctx b in
+ { sdesc = Sblock b'; sloc = elab_loc loc }
+ | _ ->
+ assert false
(* Filling in forward declaration *)
let _ = elab_funbody_f := elab_funbody
@@ -2703,7 +3048,9 @@ let _ = elab_funbody_f := elab_funbody
let elab_file prog =
reset();
- ignore (elab_definitions false (Builtins.environment()) prog);
+ let env = Builtins.environment () in
+ let elab_def env d = snd (elab_definition false false false env d) in
+ ignore (List.fold_left elab_def env prog);
let p = elaborated_program () in
Checks.unused_variables p;
Checks.unknown_attrs_program p;