From d989ab3055850bfaf2877cdf8c801a13e783ec86 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 26 Nov 2024 08:05:25 +0100 Subject: [PATCH] Investigate some ctx.m mutations (#11840) * what does this actually do? * but how about this? * how far can we take this? * I finally broke something * one more? --- src/context/display/displayTexpr.ml | 3 +- src/context/typecore.ml | 6 +- src/filters/filters.ml | 112 ++++++++++++++-------------- src/filters/filtersCommon.ml | 26 +++---- src/typing/macroContext.ml | 17 ++--- src/typing/typeloadFields.ml | 65 +++++++--------- 6 files changed, 112 insertions(+), 117 deletions(-) diff --git a/src/context/display/displayTexpr.ml b/src/context/display/displayTexpr.ml index 4ed9021e977..4a50ea9ff83 100644 --- a/src/context/display/displayTexpr.ml +++ b/src/context/display/displayTexpr.ml @@ -62,7 +62,8 @@ let actually_check_display_field ctx c cff p = let cff = TypeloadFields.transform_field (ctx,cctx) c cff (ref []) (pos cff.cff_name) in let display_modifier = Typeload.check_field_access ctx cff in let fctx = TypeloadFields.create_field_context ctx cctx cff true display_modifier in - let cf = TypeloadFields.init_field (ctx,cctx,fctx) cff in + let cf = TypeloadFields.create_class_field cctx cff in + TypeloadFields.init_field (ctx,cctx,fctx) cff cf; flush_pass ctx.g PTypeField ("check_display_field",(fst c.cl_path @ [snd c.cl_path;fst cff.cff_name])); ignore(follow cf.cf_type) diff --git a/src/context/typecore.ml b/src/context/typecore.ml index 08ffb0d085a..469345ce5f9 100644 --- a/src/context/typecore.ml +++ b/src/context/typecore.ml @@ -76,7 +76,7 @@ type typer_module = { } type typer_class = { - mutable curclass : tclass; (* TODO: should not be mutable *) + curclass : tclass; mutable tthis : t; mutable get_build_infos : unit -> (module_type * t list * class_field list) option; } @@ -150,7 +150,7 @@ and typer_expr = { } and typer_field = { - mutable curfield : tclass_field; + curfield : tclass_field; mutable locals : (string, tvar) PMap.t; mutable vthis : tvar option; mutable untyped : bool; @@ -165,7 +165,7 @@ and typer = { com : context; t : basic_types; g : typer_globals; - mutable m : typer_module; + m : typer_module; c : typer_class; f : typer_field; e : typer_expr; diff --git a/src/filters/filters.ml b/src/filters/filters.ml index e2baac24dcf..eaf1b9c70ad 100644 --- a/src/filters/filters.ml +++ b/src/filters/filters.ml @@ -251,27 +251,29 @@ let mark_switch_break_loops e = in run e -let rec fix_return_dynamic_from_void_function return_is_void e = - match e.eexpr with - | TFunction fn -> - let is_void = ExtType.is_void (follow fn.tf_type) in - let body = fix_return_dynamic_from_void_function is_void fn.tf_expr in - { e with eexpr = TFunction { fn with tf_expr = body } } - | TReturn (Some return_expr) when return_is_void && t_dynamic == follow return_expr.etype -> - let return_pos = { e.epos with pmax = return_expr.epos.pmin - 1 } in - let exprs = [ - fix_return_dynamic_from_void_function return_is_void return_expr; - { e with eexpr = TReturn None; epos = return_pos }; - ] in - { e with - eexpr = TMeta ( - (Meta.MergeBlock, [], null_pos), - mk (TBlock exprs) e.etype e.epos - ); - } - | _ -> Type.map_expr (fix_return_dynamic_from_void_function return_is_void) e - -let check_abstract_as_value e = +let fix_return_dynamic_from_void_function _ e = + let rec loop return_is_void e = match e.eexpr with + | TFunction fn -> + let is_void = ExtType.is_void (follow fn.tf_type) in + let body = loop is_void fn.tf_expr in + { e with eexpr = TFunction { fn with tf_expr = body } } + | TReturn (Some return_expr) when return_is_void && t_dynamic == follow return_expr.etype -> + let return_pos = { e.epos with pmax = return_expr.epos.pmin - 1 } in + let exprs = [ + loop return_is_void return_expr; + { e with eexpr = TReturn None; epos = return_pos }; + ] in + { e with + eexpr = TMeta ( + (Meta.MergeBlock, [], null_pos), + mk (TBlock exprs) e.etype e.epos + ); + } + | _ -> Type.map_expr (loop return_is_void) e + in + loop true e + +let check_abstract_as_value _ e = let rec loop e = match e.eexpr with | TField ({ eexpr = TTypeExpr _ }, _) -> () @@ -489,30 +491,30 @@ let destruction tctx detail_times main locals = tctx detail_times (* This has to run after DCE, or otherwise its condition always holds. *) - ["insert_save_stacks",Exceptions.insert_save_stacks tctx] + ["insert_save_stacks",Exceptions.insert_save_stacks] ) com.types; let type_filters = [ - Exceptions.patch_constructors tctx; (* TODO: I don't believe this should load_instance anything at this point... *) - check_private_path com; - Naming.apply_native_paths; - add_rtti com; - (match com.platform with | Jvm -> (fun _ -> ()) | _ -> (fun mt -> AddFieldInits.add_field_inits tctx.c.curclass.cl_path locals com mt)); - (match com.platform with Hl -> (fun _ -> ()) | _ -> add_meta_field com); - check_void_field; - (match com.platform with | Cpp -> promote_first_interface_to_super | _ -> (fun _ -> ())); - commit_features com; - (if com.config.pf_reserved_type_paths <> [] then check_reserved_type_paths com else (fun _ -> ())); + Exceptions.patch_constructors; (* TODO: I don't believe this should load_instance anything at this point... *) + (fun _ -> check_private_path com); + (fun _ -> Naming.apply_native_paths); + (fun _ -> add_rtti com); + (match com.platform with | Jvm -> (fun _ _ -> ()) | _ -> (fun tctx mt -> AddFieldInits.add_field_inits tctx.c.curclass.cl_path locals com mt)); + (match com.platform with Hl -> (fun _ _ -> ()) | _ -> (fun _ -> add_meta_field com)); + (fun _ -> check_void_field); + (fun _ -> (match com.platform with | Cpp -> promote_first_interface_to_super | _ -> (fun _ -> ()))); + (fun _ -> commit_features com); + (fun _ -> (if com.config.pf_reserved_type_paths <> [] then check_reserved_type_paths com else (fun _ -> ()))); ] in with_timer detail_times "type 3" None (fun () -> List.iter (fun t -> - begin match t with - | TClassDecl c -> - tctx.c.curclass <- c - | _ -> - () - end; - List.iter (fun f -> f t) type_filters + let tctx = match t with + | TClassDecl c -> + TyperManager.clone_for_class tctx c + | _ -> + tctx + in + List.iter (fun f -> f tctx t) type_filters ) com.types; ); com.callbacks#run com.error_ext com.callbacks#get_after_filters; @@ -705,20 +707,20 @@ let run tctx main before_destruction = NullSafety.run com new_types; (* PASS 1: general expression filters *) let filters = [ - "ForRemap",ForRemap.apply tctx; - "handle_abstract_casts",AbstractCast.handle_abstract_casts tctx; + "ForRemap",ForRemap.apply; + "handle_abstract_casts",AbstractCast.handle_abstract_casts; ] in List.iter (run_expression_filters tctx detail_times filters) new_types; let filters = [ - "local_statics",LocalStatic.run tctx; - "fix_return_dynamic_from_void_function",fix_return_dynamic_from_void_function true; - "check_local_vars_init",check_local_vars_init tctx; + "local_statics",LocalStatic.run; + "fix_return_dynamic_from_void_function",fix_return_dynamic_from_void_function; + "check_local_vars_init",check_local_vars_init; "check_abstract_as_value",check_abstract_as_value; - "Tre",if defined com Define.AnalyzerOptimize then Tre.run tctx else (fun e -> e); - "reduce_expression",Optimizer.reduce_expression tctx; - "inline_constructors",InlineConstructors.inline_constructors tctx; - "Exceptions_filter",Exceptions.filter tctx; - "captured_vars",CapturedVars.captured_vars com; + "Tre",if defined com Define.AnalyzerOptimize then Tre.run else (fun _ e -> e); + "reduce_expression",Optimizer.reduce_expression; + "inline_constructors",InlineConstructors.inline_constructors; + "Exceptions_filter",Exceptions.filter; + "captured_vars",(fun _ -> CapturedVars.captured_vars com); ] in List.iter (run_expression_filters tctx detail_times filters) new_types; (* PASS 1.5: pre-analyzer type filters *) @@ -739,13 +741,13 @@ let run tctx main before_destruction = enter_stage com CAnalyzerDone; let locals = RenameVars.init com in let filters = [ - "sanitize",Optimizer.sanitize com; - "add_final_return",if com.config.pf_add_final_return then add_final_return else (fun e -> e); + "sanitize",(fun _ e -> Optimizer.sanitize com e); + "add_final_return",(fun _ -> if com.config.pf_add_final_return then add_final_return else (fun e -> e)); "RenameVars",(match com.platform with - | Eval -> (fun e -> e) - | Jvm -> (fun e -> e) - | _ -> (fun e -> RenameVars.run tctx.c.curclass.cl_path locals e)); - "mark_switch_break_loops",mark_switch_break_loops; + | Eval -> (fun _ e -> e) + | Jvm -> (fun _ e -> e) + | _ -> (fun tctx e -> RenameVars.run tctx.c.curclass.cl_path locals e)); + "mark_switch_break_loops",(fun _ -> mark_switch_break_loops); ] in List.iter (run_expression_filters tctx detail_times filters) new_types; with_timer detail_times "callbacks" None (fun () -> diff --git a/src/filters/filtersCommon.ml b/src/filters/filtersCommon.ml index 7468f4425fc..214b8f7fa5b 100644 --- a/src/filters/filtersCommon.ml +++ b/src/filters/filtersCommon.ml @@ -55,26 +55,26 @@ let is_overridden cls field = let run_expression_filters ?(ignore_processed_status=false) ctx detail_times filters t = let com = ctx.com in - let run identifier e = + let run (ctx : typer) identifier e = List.fold_left (fun e (filter_name,f) -> - FilterContext.with_timer detail_times filter_name identifier (fun () -> f e) + FilterContext.with_timer detail_times filter_name identifier (fun () -> f ctx e) ) e filters in match t with | TClassDecl c when is_removable_class c -> () | TClassDecl c -> - ctx.c.curclass <- c; - ctx.m <- TypeloadModule.make_curmod ctx.com ctx.g c.cl_module; - let rec process_field f = - if ignore_processed_status || not (has_class_field_flag f CfPostProcessed) then begin - ctx.f.curfield <- f; - (match f.cf_expr with - | Some e when not (is_removable_field com f) -> - let identifier = Printf.sprintf "%s.%s" (s_type_path c.cl_path) f.cf_name in - f.cf_expr <- Some (rec_stack_loop AbstractCast.cast_stack f (run (Some identifier)) e); + let ctx = TyperManager.clone_for_module ctx (TypeloadModule.make_curmod ctx.com ctx.g c.cl_module) in + let ctx = TyperManager.clone_for_class ctx c in + let rec process_field cf = + if ignore_processed_status || not (has_class_field_flag cf CfPostProcessed) then begin + let ctx = TyperManager.clone_for_field ctx cf cf.cf_params in + (match cf.cf_expr with + | Some e when not (is_removable_field com cf) -> + let identifier = Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name in + cf.cf_expr <- Some (rec_stack_loop AbstractCast.cast_stack cf (run ctx (Some identifier)) e); | _ -> ()); end; - List.iter process_field f.cf_overloads + List.iter process_field cf.cf_overloads in List.iter process_field c.cl_ordered_fields; List.iter process_field c.cl_ordered_statics; @@ -85,7 +85,7 @@ let run_expression_filters ?(ignore_processed_status=false) ctx detail_times fil | None -> () | Some e -> let identifier = Printf.sprintf "%s.__init__" (s_type_path c.cl_path) in - TClass.set_cl_init c (run (Some identifier) e)) + TClass.set_cl_init c (run ctx (Some identifier) e)) | TEnumDecl _ -> () | TTypeDecl _ -> () | TAbstractDecl _ -> () diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index 7b5c23f3e6e..7b0fde048c0 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -608,10 +608,10 @@ and flush_macro_context mint mctx = mctx.com.Common.modules <- modules; (* we should maybe ensure that all filters in Main are applied. Not urgent atm *) let expr_filters = [ - "handle_abstract_casts",AbstractCast.handle_abstract_casts mctx; - "local_statics",LocalStatic.run mctx; - "Exceptions",Exceptions.filter mctx; - "captured_vars",CapturedVars.captured_vars mctx.com; + "handle_abstract_casts",AbstractCast.handle_abstract_casts; + "local_statics",LocalStatic.run; + "Exceptions",Exceptions.filter; + "captured_vars",(fun _ -> CapturedVars.captured_vars mctx.com); ] in (* some filters here might cause side effects that would break compilation server. @@ -752,7 +752,7 @@ let load_macro_module mctx com cpath display p = let old = mctx.com.display in if display then mctx.com.display <- com.display; let mloaded = TypeloadModule.load_module ~origin:MDepFromMacro mctx m p in - mctx.m <- { + (* mctx.m <- { curmod = mloaded; import_resolution = new resolution_list ["import";s_type_path cpath]; own_resolution = None; @@ -760,7 +760,7 @@ let load_macro_module mctx com cpath display p = module_using = []; import_statements = []; is_display_file = (com.display.dms_kind <> DMNone && DisplayPosition.display_position#is_in_file (Path.UniqueKey.lazy_key mloaded.m_extra.m_file)); - }; + }; *) mloaded,(fun () -> mctx.com.display <- old) let load_macro'' com mctx display cpath f p = @@ -794,7 +794,7 @@ let load_macro'' com mctx display cpath f p = restore(); if not com.is_macro_context then flush_macro_context mint mctx; mctx.com.cached_macros#add (cpath,f) meth; - mctx.m <- { + (* mctx.m <- { curmod = null_module; import_resolution = new resolution_list ["import";s_type_path cpath]; own_resolution = None; @@ -802,7 +802,7 @@ let load_macro'' com mctx display cpath f p = module_using = []; import_statements = []; is_display_file = false; - }; + }; *) t(); meth @@ -1017,7 +1017,6 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = e let call_macro mctx args margs call p = - mctx.c.curclass <- null_class; let el, _ = CallUnification.unify_call_args mctx args margs t_dynamic p false false false in call (List.map (fun e -> try Interp.make_const e with Exit -> raise_typing_error "Argument should be a constant" e.epos) el) diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index d280d99424c..4724219d2a2 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -538,10 +538,10 @@ let create_field_context ctx cctx cff is_display_file display_modifier = if fctx.is_display_field then cctx.has_display_field <- true; fctx -let create_typer_context_for_field ctx cctx fctx cff = +let create_typer_context_for_field ctx cctx fctx cff cf = DeprecationCheck.check_is ctx.com ctx.m.curmod ctx.c.curclass.cl_meta cff.cff_meta (fst cff.cff_name) cff.cff_meta (snd cff.cff_name); let params = if fctx.is_static && not fctx.is_abstract_member && not (Meta.has Meta.LibType cctx.tclass.cl_meta) (* TODO: remove this *) then [] else ctx.type_params in - let ctx = TyperManager.clone_for_field ctx null_field params in + let ctx = TyperManager.clone_for_field ctx cf params in let c = cctx.tclass in if (fctx.is_abstract && not (has_meta Meta.LibType c.cl_meta)) then begin @@ -560,7 +560,7 @@ let create_typer_context_for_field ctx cctx fctx cff = end; ctx -let is_public (ctx,cctx) access parent = +let is_public cctx access parent = let c = cctx.tclass in if List.mem_assoc APrivate access then false @@ -890,7 +890,7 @@ let load_variable_type_hint ctx fctx eo p = function | Some t -> load_type_hint ctx p LoadNormal (Some t) -let create_variable (ctx,cctx,fctx) c f t eo p = +let create_variable (ctx,cctx,fctx) c f cf t eo p = let is_abstract_enum_field = List.mem_assoc AEnum f.cff_access in if fctx.is_abstract_member && not is_abstract_enum_field then raise_typing_error "Cannot declare member variable in abstract" p; if fctx.is_inline && not fctx.is_static then invalid_modifier ctx.com fctx "inline" "non-static variable" p; @@ -910,12 +910,8 @@ let create_variable (ctx,cctx,fctx) c f t eo p = else { v_read = AccNormal ; v_write = AccNormal } in - let cf = { - (mk_field (fst f.cff_name) ~public:(is_public (ctx,cctx) f.cff_access None) t f.cff_pos (pos f.cff_name)) with - cf_doc = f.cff_doc; - cf_meta = f.cff_meta; - cf_kind = Var kind; - } in + cf.cf_type <- t; + cf.cf_kind <- Var kind; if fctx.is_final then begin if missing_initialization && not fctx.is_static then cctx.uninitialized_final <- cf :: cctx.uninitialized_final; @@ -927,7 +923,6 @@ let create_variable (ctx,cctx,fctx) c f t eo p = add_class_field_flag cf CfImpl; end; if is_abstract_enum_field then add_class_field_flag cf CfEnum; - ctx.f.curfield <- cf; TypeBinding.bind_var ctx cctx fctx cf eo; cf @@ -1180,7 +1175,7 @@ let setup_args_ret ctx cctx fctx name fd p = let args = new FunctionArguments.function_arguments ctx.com type_arg is_extern fctx.is_display_field abstract_this fd.f_args in args,ret -let create_method (ctx,cctx,fctx) c f fd p = +let create_method (ctx,cctx,fctx) c f cf fd p = let name = fst f.cff_name in let params = TypeloadFunction.type_function_params ctx fd TPHMethod name p in if fctx.is_generic then begin @@ -1257,13 +1252,10 @@ let create_method (ctx,cctx,fctx) c f fd p = ctx.type_params <- params @ ctx.type_params; let args,ret = setup_args_ret ctx cctx fctx (fst f.cff_name) fd p in let t = TFun (args#for_type,ret) in - let cf = { - (mk_field name ~public:(is_public (ctx,cctx) f.cff_access parent) t f.cff_pos (pos f.cff_name)) with - cf_doc = f.cff_doc; - cf_meta = f.cff_meta; - cf_kind = Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal); - cf_params = params; - } in + cf.cf_type <- t; + cf.cf_kind <- Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal); + cf.cf_params <- params; + if is_public cctx f.cff_access parent then add_class_field_flag cf CfPublic; if fctx.is_final then add_class_field_flag cf CfFinal; if fctx.is_extern then add_class_field_flag cf CfExtern; if fctx.is_abstract then begin @@ -1325,7 +1317,6 @@ let create_method (ctx,cctx,fctx) c f fd p = if fctx.field_kind = CfrConstructor then FunConstructor else if fctx.is_static then FunStatic else FunMember in init_meta_overloads ctx (Some c) cf; - ctx.f.curfield <- cf; if fctx.do_bind then TypeBinding.bind_method ctx cctx fctx fmode cf t args ret fd.f_expr (match fd.f_expr with Some e -> snd e | None -> f.cff_pos) else begin @@ -1348,13 +1339,14 @@ let create_method (ctx,cctx,fctx) c f fd p = end; cf -let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = +let create_property (ctx,cctx,fctx) c f cf (get,set,t,eo) p = let name = fst f.cff_name in let ret = (match t, eo with | None, None -> raise_typing_error "Property requires type-hint or initialization" p; | None, _ -> mk_mono() | Some t, _ -> load_type_hint ctx p LoadNormal (Some t) ) in + cf.cf_type <- ret; let t_get,t_set = match cctx.abstract with | Some a when fctx.is_abstract_member -> if Meta.has Meta.IsVar f.cff_meta then raise_typing_error "Abstract properties cannot be real variables" f.cff_pos; @@ -1369,11 +1361,6 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = end else get_overloads ctx.com c m in - let cf = { - (mk_field name ~public:(is_public (ctx,cctx) f.cff_access None) ret f.cff_pos (pos f.cff_name)) with - cf_doc = f.cff_doc; - cf_meta = f.cff_meta; - } in if fctx.is_abstract_member then add_class_field_flag cf CfImpl; let check_method m t is_getter = try @@ -1488,7 +1475,6 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = cf.cf_kind <- Var { v_read = get; v_write = set }; if fctx.is_extern then add_class_field_flag cf CfExtern; if List.mem_assoc AEnum f.cff_access then add_class_field_flag cf CfEnum; - ctx.f.curfield <- cf; TypeBinding.bind_var ctx cctx fctx cf eo; cf @@ -1507,10 +1493,10 @@ let reject_final_static_method ctx cctx fctx f = in invalid_modifier_combination fctx ctx.com fctx "final" "static" p -let init_field (ctx,cctx,fctx) f = +let init_field (ctx,cctx,fctx) f cf = let c = cctx.tclass in let name = fst f.cff_name in - TypeloadCheck.check_global_metadata ctx f.cff_meta (fun m -> f.cff_meta <- m :: f.cff_meta) c.cl_module.m_path c.cl_path (Some name); + TypeloadCheck.check_global_metadata ctx f.cff_meta (fun m -> cf.cf_meta <- m :: cf.cf_meta) c.cl_module.m_path c.cl_path (Some name); let p = f.cff_pos in if not (has_class_flag c CExtern) && not (Meta.has Meta.Native f.cff_meta) then Naming.check_field_name ctx.com name p; List.iter (fun acc -> @@ -1539,17 +1525,16 @@ let init_field (ctx,cctx,fctx) f = let cf = match f.cff_kind with | FVar (t,e) -> - create_variable (ctx,cctx,fctx) c f t e p + create_variable (ctx,cctx,fctx) c f cf t e p | FFun fd -> reject_final_static_method ctx cctx fctx f; - create_method (ctx,cctx,fctx) c f fd p + create_method (ctx,cctx,fctx) c f cf fd p | FProp (get,set,t,eo) -> - create_property (ctx,cctx,fctx) c f (get,set,t,eo) p + create_property (ctx,cctx,fctx) c f cf (get,set,t,eo) p in (if (fctx.is_static || fctx.is_macro && ctx.com.is_macro_context) then add_class_field_flag cf CfStatic); if Meta.has Meta.InheritDoc cf.cf_meta then - delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) cf); - cf + delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) cf) let check_overload ctx f fs is_extern_class = try @@ -1612,6 +1597,13 @@ let check_functional_interface ctx c = add_class_flag c CFunctionalInterface; ctx.com.functional_interface_lut#add c.cl_path (c,cf) +let create_class_field cctx f = + let cf = {(mk_field (fst f.cff_name) ~public:(is_public cctx f.cff_access None) t_dynamic f.cff_pos (pos f.cff_name)) with + cf_doc = f.cff_doc; + cf_meta = f.cff_meta + } in + cf + let init_class ctx_c cctx c p herits fields = let com = ctx_c.com in if cctx.is_class_debug then print_endline ("Created class context: " ^ dump_class_context cctx); @@ -1658,10 +1650,11 @@ let init_class ctx_c cctx c p herits fields = let p = f.cff_pos in let display_modifier = Typeload.check_field_access ctx_c f in let fctx = create_field_context ctx_c cctx f ctx_c.m.is_display_file display_modifier in - let ctx = create_typer_context_for_field ctx_c cctx fctx f in + let cf = create_class_field cctx f in + let ctx = create_typer_context_for_field ctx_c cctx fctx f cf in try if fctx.is_field_debug then print_endline ("Created field context: " ^ dump_field_context fctx); - let cf = init_field (ctx,cctx,fctx) f in + init_field (ctx,cctx,fctx) f cf; if fctx.field_kind = CfrInit then begin if !has_init then display_error ctx.com ("Duplicate class field declaration : " ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name) cf.cf_name_pos