From b960c83725d7e185ac5c6e3c0d6043c7dcd2f556 Mon Sep 17 00:00:00 2001 From: Jacques-Henri Jourdan Date: Sun, 1 Nov 2015 22:32:23 +0100 Subject: Better handling of old-style K&R function declarations: - Added a Cabs.PROTO_OLD constructor to Cabs.decl_type - Refactored the Parser.vy and pre_parser.mly grammars - Rewritten the conversion of old function definitions to new-style --- cparser/Cabs.v | 4 +- cparser/Cabshelper.ml | 10 +- cparser/Elab.ml | 196 +++++++++++++++++++++------------------- cparser/Parser.vy | 146 ++++++++++++++++-------------- cparser/pre_parser.mly | 178 ++++++++++++++++++------------------ test/regression/Results/parsing | 10 ++ test/regression/parsing.c | 62 ++++++++++++- 7 files changed, 345 insertions(+), 261 deletions(-) diff --git a/cparser/Cabs.v b/cparser/Cabs.v index 6d9e95d5..ab53a3a8 100644 --- a/cparser/Cabs.v +++ b/cparser/Cabs.v @@ -81,6 +81,7 @@ with decl_type := | PTR : list cvspec -> decl_type -> decl_type (* The bool is true for variable length parameters. *) | PROTO : decl_type -> list parameter * bool -> decl_type + | PROTO_OLD : decl_type -> list string -> decl_type with parameter := | PARAM : list spec_elem -> option string -> decl_type -> list attribute -> cabsloc -> parameter @@ -190,8 +191,7 @@ Definition asm_flag := (bool * list char_code)%type. ** Declaration definition (at toplevel) *) Inductive definition := - | FUNDEF : list spec_elem -> name -> statement -> cabsloc -> definition - | KRFUNDEF : list spec_elem -> name -> list string -> list definition -> statement -> cabsloc -> definition + | FUNDEF : list spec_elem -> name -> list definition -> statement -> cabsloc -> definition | DECDEF : init_name_group -> cabsloc -> definition (* global variable(s), or function prototype *) | PRAGMA : string -> cabsloc -> definition diff --git a/cparser/Cabshelper.ml b/cparser/Cabshelper.ml index 5e6a19d0..b3782ba8 100644 --- a/cparser/Cabshelper.ml +++ b/cparser/Cabshelper.ml @@ -46,8 +46,7 @@ let rec isTypedef = function let get_definitionloc (d : definition) : cabsloc = match d with - | FUNDEF(_, _, _, l) -> l - | KRFUNDEF(_, _, _, _, _, l) -> l + | FUNDEF(_, _, _, _, l) -> l | DECDEF(_, l) -> l | PRAGMA(_, l) -> l @@ -78,10 +77,3 @@ let string_of_cabsloc l = let format_cabsloc pp l = Format.fprintf pp "%s:%d" l.filename l.lineno - -let rec append_decltype dt1 dt2 = - match dt1 with - | JUSTBASE -> dt2 - | ARRAY(dt, attr, sz) -> ARRAY(append_decltype dt dt2, attr, sz) - | PTR(attr, dt) -> PTR(attr, append_decltype dt dt2) - | PROTO(dt, params) -> PROTO(append_decltype dt dt2, params) diff --git a/cparser/Elab.ml b/cparser/Elab.ml index 4d3d1d02..d078cdac 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -518,9 +518,9 @@ and elab_cvspecs env cv_specs = (* Elaboration of a type declarator. C99 section 6.7.5. *) -and elab_type_declarator loc env ty = function +and elab_type_declarator loc env ty kr_ok = function | Cabs.JUSTBASE -> - (ty, env) + ((ty, None), env) | Cabs.ARRAY(d, cv_specs, sz) -> let a = elab_cvspecs env cv_specs in let sz' = @@ -536,32 +536,41 @@ and elab_type_declarator loc env ty = function | None -> error loc "array size is not a compile-time constant"; Some 1L in (* produces better error messages later *) - elab_type_declarator loc env (TArray(ty, sz', a)) d + elab_type_declarator loc env (TArray(ty, sz', a)) kr_ok d | Cabs.PTR(cv_specs, d) -> let a = elab_cvspecs env cv_specs in - elab_type_declarator loc env (TPtr(ty, a)) d + elab_type_declarator loc env (TPtr(ty, a)) kr_ok d | Cabs.PROTO(d, (params, vararg)) -> - begin match unroll env ty with - | TArray _ | TFun _ -> - error loc "illegal function return type@ %a" Cprint.typ ty - | _ -> () - end; - let params' = elab_parameters env params in - elab_type_declarator loc env (TFun(ty, params', vararg, [])) d + begin match unroll env ty with + | TArray _ | TFun _ -> + error loc "Illegal function return type@ %a" Cprint.typ ty + | _ -> () + end; + let params' = elab_parameters env params in + elab_type_declarator loc env (TFun(ty, Some params', vararg, [])) kr_ok d + | Cabs.PROTO_OLD(d, params) -> + begin match unroll env ty with + | TArray _ | TFun _ -> + error loc "Illegal function return type@ %a" Cprint.typ ty + | _ -> () + end; + match params with + | [] -> + elab_type_declarator loc env (TFun(ty, None, false, [])) kr_ok d + | _ -> + if not kr_ok || d <> Cabs.JUSTBASE then + fatal_error loc "Illegal old-style K&R function definition"; + ((TFun(ty, None, false, []), Some params), env) (* Elaboration of parameters in a prototype *) and elab_parameters env params = - match params with - | [] -> (* old-style K&R prototype *) - None - | _ -> - (* Prototype introduces a new scope *) - let (vars, _) = 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 - | [ ( {name=""}, t) ] when is_void_type env t -> Some [] - | _ -> Some vars + (* Prototype introduces a new scope *) + let (vars, _) = 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 + | [ ( {name=""}, t) ] when is_void_type env t -> [] + | _ -> vars (* Elaboration of a function parameter *) @@ -569,7 +578,7 @@ and elab_parameter env (PARAM (spec, id, decl, attr, loc)) = let (sto, inl, tydef, bty, env1) = elab_specifier loc env spec in if tydef then error loc "'typedef' used in function parameter"; - let (ty, env2) = elab_type_declarator loc env1 bty decl in + let ((ty, _), env2) = elab_type_declarator loc env1 bty false decl in let ty = add_attributes_type (elab_attributes env attr) ty in if sto <> Storage_default && sto <> Storage_register then error loc @@ -586,13 +595,13 @@ and elab_parameter env (PARAM (spec, id, decl, attr, loc)) = (* Elaboration of a (specifier, Cabs "name") pair *) -and elab_name env spec (Name (id, decl, attr, loc)) = +and elab_fundef_name env spec (Name (id, decl, attr, loc)) = let (sto, inl, tydef, bty, env') = elab_specifier loc env spec in if tydef then error loc "'typedef' is forbidden here"; - let (ty, env'') = elab_type_declarator loc env' bty decl in + let ((ty, kr_params), env'') = elab_type_declarator loc env' bty true decl in let a = elab_attributes env attr in - (id, sto, inl, add_attributes_type a ty, env'') + (id, sto, inl, add_attributes_type a ty, kr_params, env'') (* Elaboration of a name group. C99 section 6.7.6 *) @@ -604,8 +613,8 @@ and elab_name_group loc env (spec, namelist) = if inl then error loc "'inline' is forbidden here"; let elab_one_name env (Name (id, decl, attr, loc)) = - let (ty, env1) = - elab_type_declarator loc env bty decl in + let ((ty, _), env1) = + elab_type_declarator loc env bty false decl in let a = elab_attributes env attr in ((id, add_attributes_type a ty), env1) in (mmap elab_one_name env' namelist, sto) @@ -616,8 +625,8 @@ and elab_init_name_group loc env (spec, namelist) = let (sto, inl, tydef, bty, env') = elab_specifier ~only:(namelist=[]) loc env spec in let elab_one_name env (Init_name (Name (id, decl, attr, loc), init)) = - let (ty, env1) = - elab_type_declarator loc env bty decl in + let ((ty, _), env1) = + elab_type_declarator loc env bty false decl in let a = elab_attributes env attr in if inl && not (is_function_type env ty) then error loc "'inline' can only appear on functions"; @@ -818,7 +827,7 @@ and elab_enum only loc tag optmembers attrs env = let elab_type loc env spec decl = let (sto, inl, tydef, bty, env') = elab_specifier loc env spec in - let (ty, env'') = elab_type_declarator loc env' bty decl in + let ((ty, _), env'') = elab_type_declarator loc env' bty false decl in if sto <> Storage_default || inl || tydef then error loc "'typedef', 'extern', 'static', 'register' and 'inline' are meaningless in cast"; (ty, env'') @@ -1737,8 +1746,8 @@ let elab_expr loc env a = match args, params with | [], [] -> [] | [], _::_ -> err "not enough arguments in function call"; [] - | _::_, [] -> - if vararg + | _::_, [] -> + if vararg then args else (err "too many arguments in function call"; args) | arg1 :: argl, (_, ty_p) :: paraml -> @@ -1881,20 +1890,70 @@ let enter_decdefs local loc env sto dl = let (decls, env') = List.fold_left enter_decdef ([], env) dl in (List.rev decls, env') -let elab_fundef env spec name body loc = - let (s, sto, inline, ty, env1) = elab_name env spec name in +let elab_fundef env spec name defs body loc = + let (s, sto, inline, ty, kr_params, env1) = elab_fundef_name env spec name in if sto = Storage_register then - fatal_error loc "a function definition cannot have 'register' storage class"; - (* Fix up the type. We can have params = None but only for an - old-style parameterless function "int f() {...}" *) - let ty = - match ty with - | TFun(ty_ret, None, vararg, attr) -> TFun(ty_ret, Some [], vararg, attr) - | _ -> ty in + fatal_error loc "A function definition cannot have 'register' storage class"; + begin match kr_params, defs with + | None, d::_ -> + error (get_definitionloc d) + "Old-style parameter declaration in a new-style function definition" + | _ -> () + end; + let (ty, env1) = + match ty, kr_params with + | TFun(ty_ret, None, vararg, attr), None -> + (TFun(ty_ret, Some [], vararg, attr), env1) + | ty, None -> + (ty, env1) + | TFun(ty_ret, None, false, attr), Some params -> + warning loc "Non-prototype, pre-standard function definition.@ Converting to prototype form"; + (* Check that the parameters have unique names *) + List.iter (fun id -> + if List.length (List.filter ((=) id) params) > 1 then + fatal_error loc "Parameter '%s' appears more than once in function declaration" id) + params; + (* 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; + if ie <> NO_INIT then + error loc' "Illegal initialization of function parameter '%s'" s; + name + in + (* Convert old-style K&R function definition to modern prototyped form *) + let elab_kr_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 loc' env (spec', name_list) in + if sto <> Storage_default && sto <> Storage_register then + error loc' "'extern' or 'static' storage not supported for function parameter"; + paramsenv + | d -> + (* Should never be produced by the parser *) + fatal_error (get_definitionloc d) + "Illegal declaration of function parameter" in + let (kr_params_defs, env1) = mmap elab_kr_param_def env1 defs in + let kr_params_defs = List.concat kr_params_defs in + let rec search_param_type param = + match List.filter (fun (p, _) -> p = param) kr_params_defs with + | [] -> + (* Parameter is not declared, defaults to "int" in ISO C90, + is an error in ISO C99. Just emit a warning. *) + warning loc "Type of '%s' defaults to 'int'" param; + (Env.fresh_ident param, TInt (IInt, [])) + | (_,ty)::q -> + if q <> [] then error loc "Parameter '%s' defined more than once" param; + (Env.fresh_ident param, argument_conversion env1 ty) + in + let params' = List.map search_param_type params in + (TFun(ty_ret, Some params', false, attr), env1) + | _, Some params -> assert false + in (* Extract info from type *) let (ty_ret, params, vararg, attr) = match ty with - | TFun(ty_ret, Some params, vararg, attr) -> + | TFun(ty_ret, Some params, vararg, attr) -> if wrap incomplete_type loc env1 ty_ret && not (is_void_type env ty_ret) then fatal_error loc "return type is an incomplete type"; (ty_ret, params, vararg, attr) @@ -1938,66 +1997,19 @@ let elab_fundef env spec name body loc = emit_elab env1 loc (Gfundef fn); env1 -let elab_kr_fundef env spec name params defs body loc = - warning loc "Non-prototype, pre-standard function definition.@ Converting to prototype form"; - (* Check that the declarations only declare parameters *) - let check_one_decl (Init_name(Name(s, dty, attrs, loc'), ie)) = - if not (List.mem s params) then - error loc' "Declaration of '%s' which is not a function parameter" s; - if ie <> NO_INIT then - error loc' "Illegal initialization of function parameter '%s'" s in - let check_decl = function - | DECDEF((spec', name_init_list), loc') -> - List.iter check_one_decl name_init_list - | d -> - (* Should never be produced by the parser *) - fatal_error (get_definitionloc d) - "Illegal declaration of function parameter" in - List.iter check_decl defs; - (* Convert old-style K&R function definition to modern prototyped form *) - let rec convert_param param = function - | [] -> - (* Parameter is not declared, defaults to "int" in ISO C90, - is an error in ISO C99. Just emit a warning. *) - warning loc "Type of '%s' defaults to 'int'" param; - PARAM([SpecType Tint], Some param, JUSTBASE, [], loc) - | DECDEF((spec', name_init_list), loc') :: defs -> - let rec convert = function - | [] -> convert_param param defs - | Init_name(Name(s, dty, attrs, loc''), ie) :: l -> - if s = param - then PARAM(spec', Some param, dty, attrs, loc'') - else convert l - in convert name_init_list - | _ -> - assert false (* checked earlier *) in - let params' = - List.map (fun p -> convert_param p defs) params in - let name' = - let (Name(s, dty, attr, loc')) = name in - Name(s, append_decltype dty (PROTO(JUSTBASE, (params', false))), - attr, loc') in - (* Elaborate the prototyped form *) - elab_fundef env spec name' body loc - let rec elab_definition (local: bool) (env: Env.t) (def: Cabs.definition) : decl list * Env.t = match def with (* "int f(int x) { ... }" *) - | FUNDEF(spec, name, body, loc) -> - if local then error loc "local definition of a function"; - let env1 = elab_fundef env spec name body loc in - ([], env1) - (* "int f(x, y) double y; { ... }" *) - | KRFUNDEF(spec, name, params, defs, body, loc) -> + | FUNDEF(spec, name, defs, body, loc) -> if local then error loc "local definition of a function"; - let env1 = elab_kr_fundef env spec name params defs body loc in + let env1 = elab_fundef env spec name defs body loc in ([], env1) (* "int x = 12, y[10], *z" *) | DECDEF(init_name_group, loc) -> - let ((dl, env1), sto, tydef) = + let ((dl, env1), sto, tydef) = elab_init_name_group loc env init_name_group in if tydef then let env2 = enter_typedefs loc env1 sto dl diff --git a/cparser/Parser.vy b/cparser/Parser.vy index 7c0bfb55..16f6a0ef 100644 --- a/cparser/Parser.vy +++ b/cparser/Parser.vy @@ -52,6 +52,7 @@ Require Import List. %type argument_expression_list %type declaration %type declaration_specifiers +%type declaration_specifiers_typespec_opt %type init_declarator_list %type init_declarator %type storage_class_specifier @@ -65,9 +66,9 @@ Require Import List. %type enumerator_list %type enumerator %type enumeration_constant -%type type_qualifier +%type type_qualifier type_qualifier_noattr %type function_specifier -%type declarator direct_declarator +%type declarator declarator_noattrend direct_declarator %type<(decl_type -> decl_type) * cabsloc> pointer %type type_qualifier_list %type parameter_type_list @@ -95,7 +96,6 @@ Require Import List. %type gcc_attribute %type gcc_attribute_list %type gcc_attribute_word -%type old_function_declarator direct_old_function_declarator %type identifier_list %type asm_flags %type