Quellcode durchsuchen

Support `inline call()` and `inline new C()` expressions (#7425)

* support `inline call()`

* [typer] support `inline call` on local functions

* use Meta.Inline

* clean up json output

* support `inline new C()`

* un-fuck-up the merge

* detect failed `inline new`
Simon Krajewski vor 7 Jahren
Ursprung
Commit
97f2641834

+ 10 - 10
src/codegen/gencommon/closuresToClass.ml

@@ -191,29 +191,29 @@ let traverse gen ?tparam_anon_decl ?tparam_anon_acc (handle_anon_func:texpr->tfu
 				| None -> Type.map_expr run e
 				| Some tparam_anon_decl ->
 					(match (vv, ve) with
-						| ({ v_extra = Some( _ :: _, _) } as v), Some ({ eexpr = TFunction tf } as f)
-						| ({ v_extra = Some( _ :: _, _) } as v), Some { eexpr = TArrayDecl([{ eexpr = TFunction tf } as f]) | TCall({ eexpr = TIdent "__array__" }, [{ eexpr = TFunction tf } as f]) } -> (* captured transformation *)
+						| ({ v_extra = Some( _ :: _, _,_) } as v), Some ({ eexpr = TFunction tf } as f)
+						| ({ v_extra = Some( _ :: _, _,_) } as v), Some { eexpr = TArrayDecl([{ eexpr = TFunction tf } as f]) | TCall({ eexpr = TIdent "__array__" }, [{ eexpr = TFunction tf } as f]) } -> (* captured transformation *)
 							tparam_anon_decl v f { tf with tf_expr = run tf.tf_expr };
 							{ e with eexpr = TBlock([]) }
 						| _ ->
 							Type.map_expr run { e with eexpr = TVar(vv, ve) })
 					)
-			| TBinop(OpAssign, { eexpr = TLocal({ v_extra = Some(_ :: _, _) } as v)}, ({ eexpr= TFunction tf } as f)) when is_some tparam_anon_decl ->
+			| TBinop(OpAssign, { eexpr = TLocal({ v_extra = Some(_ :: _, _,_) } as v)}, ({ eexpr= TFunction tf } as f)) when is_some tparam_anon_decl ->
 				(match tparam_anon_decl with
 					| None -> assert false
 					| Some tparam_anon_decl ->
 						tparam_anon_decl v f { tf with tf_expr = run tf.tf_expr };
 						{ e with eexpr = TBlock([]) }
 				)
-			| TLocal ({ v_extra = Some( _ :: _, _) } as v) ->
+			| TLocal ({ v_extra = Some( _ :: _, _, _) } as v) ->
 				(match tparam_anon_acc with
 				| None -> Type.map_expr run e
 				| Some tparam_anon_acc -> tparam_anon_acc v e false)
-			| TArray ( ({ eexpr = TLocal ({ v_extra = Some( _ :: _, _) } as v) } as expr), _) -> (* captured transformation *)
+			| TArray ( ({ eexpr = TLocal ({ v_extra = Some( _ :: _, _, _) } as v) } as expr), _) -> (* captured transformation *)
 				(match tparam_anon_acc with
 				| None -> Type.map_expr run e
 				| Some tparam_anon_acc -> tparam_anon_acc v { expr with etype = e.etype } false)
-			| TMeta((Meta.Custom ":tparamcall",_,_),({ eexpr=TLocal ({ v_extra = Some( _ :: _, _) } as v) } as expr)) ->
+			| TMeta((Meta.Custom ":tparamcall",_,_),({ eexpr=TLocal ({ v_extra = Some( _ :: _, _, _) } as v) } as expr)) ->
 				(match tparam_anon_acc with
 				| None -> Type.map_expr run e
 				| Some tparam_anon_acc -> tparam_anon_acc v expr true)
@@ -333,12 +333,12 @@ let get_captured expr =
 				Type.iter traverse expr
 			| TVar (v, opt) ->
 				(match v.v_extra with
-					| Some(_ :: _, _) -> ()
+					| Some(_ :: _, _, _) -> ()
 					| _ ->
 						check_params v.v_type);
 				Hashtbl.add ignored v.v_id v;
 				ignore(Option.map traverse opt)
-			| TLocal { v_extra = Some( (_ :: _ ),_) } ->
+			| TLocal { v_extra = Some( (_ :: _ ),_,_) } ->
 				()
 			| TLocal(( { v_capture = true } ) as v) ->
 				(if not (Hashtbl.mem ignored v.v_id || Hashtbl.mem ret v.v_id) then begin check_params v.v_type; Hashtbl.replace ret v.v_id expr end);
@@ -523,7 +523,7 @@ let configure gen ft =
 
 		(match tvar with
 		| None -> ()
-		| Some ({ v_extra = Some(_ :: _, _) } as v) ->
+		| Some ({ v_extra = Some(_ :: _, _, _) } as v) ->
 			Hashtbl.add tvar_to_cdecl v.v_id (cls,captured)
 		| _ -> ());
 
@@ -591,7 +591,7 @@ let configure gen ft =
 				| _ -> assert false) captured
 			in
 			let types = match v.v_extra with
-				| Some(t,_) -> t
+				| Some(t,_,_) -> t
 				| _ -> assert false
 			in
 			let monos = List.map (fun _ -> mk_mono()) types in

+ 1 - 1
src/context/abstractCast.ml

@@ -237,7 +237,7 @@ let handle_abstract_casts ctx e =
 				let rec find_abstract e t = match follow t,e.eexpr with
 					| TAbstract(a,pl),_ when Meta.has Meta.MultiType a.a_meta -> a,pl,e
 					| _,TCast(e1,None) -> find_abstract e1 e1.etype
-					| _,TLocal {v_extra = Some(_,Some e')} ->
+					| _,TLocal {v_extra = Some(_,e',true)} ->
 						begin match follow e'.etype with
 						| TAbstract(a,pl) when Meta.has Meta.MultiType a.a_meta -> a,pl,mk (TCast(e,None)) e'.etype e.epos
 						| _ -> raise Not_found

+ 1 - 1
src/context/typecore.ml

@@ -129,7 +129,7 @@ exception Forbid_package of (string * path * pos) * pos list * string
 
 exception WithTypeError of error_msg * pos
 
-let make_call_ref : (typer -> texpr -> texpr list -> t -> pos -> texpr) ref = ref (fun _ _ _ _ _ -> assert false)
+let make_call_ref : (typer -> texpr -> texpr list -> t -> ?force_inline:bool -> pos -> texpr) ref = ref (fun _ _ _ _ ?force_inline:bool _ -> assert false)
 let type_expr_ref : (typer -> expr -> WithType.t -> texpr) ref = ref (fun _ _ _ -> assert false)
 let type_block_ref : (typer -> expr list -> WithType.t -> pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
 let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> assert false)

+ 9 - 8
src/core/json/genjson.ml

@@ -252,13 +252,14 @@ and generate_type_parameter ctx (s,t) =
 (* texpr *)
 
 and generate_tvar ctx v =
-	let generate_extra (params,eo) = jobject (
-		("params",jlist (generate_type_parameter ctx) params) ::
-		(match eo with
-		| None -> []
-		| Some e ->	["expr",jobject [
-			("string",jstring (s_expr_pretty false "" false (s_type (print_context())) e))
-		]]);
+	let generate_extra (params,e,inline) = jobject (
+		[
+			"params",jlist (generate_type_parameter ctx) params;
+			"expr",jobject [
+				("string",jstring (s_expr_pretty false "" false (s_type (print_context())) e))
+			];
+			"isInline",jbool inline;
+		]
 	) in
 	let fields = [
 		"id",jint v.v_id;
@@ -268,7 +269,7 @@ and generate_tvar ctx v =
 		"extra",jopt generate_extra v.v_extra;
 		"meta",generate_metadata ctx v.v_meta;
 		"pos",generate_pos ctx v.v_pos;
-		"isInline",jbool (match v.v_extra with Some (_,Some _) -> true | _ -> false);
+		"isInline",jbool (match v.v_extra with Some (_,_,b) -> b | _ -> false);
 		"isFinal",jbool v.v_final;
 	] in
 	let origin_to_int = function

+ 1 - 1
src/core/meta.ml

@@ -275,7 +275,7 @@ let get_info = function
 	| ImplicitCast -> ":implicitCast",("Generated automatically on the AST when an implicit abstract cast happens",[UsedInternally; UsedOn TExpr])
 	| Include -> ":include",("",[Platform Cpp])
 	| InitPackage -> ":initPackage",("Some weird thing for Genjs we want to remove someday",[UsedInternally; Platform Js])
-	| Inline -> ":inline",("",[])
+	| Inline -> ":inline",("Inserted by the parser in case of `inline expr` and `inline function`",[UsedOn TExpr])
 	| InlineConstructorArgument _ -> ":inlineConstructorArgument",("Internally used to mark expressions that were passed as arguments of an inlined constructor",[UsedInternally])
 	| Internal -> ":internal",("Generates the annotated field/class with 'internal' access",[Platforms [Java;Cs]; UsedOnEither[TClass;TEnum;TClassField]])
 	| IsVar -> ":isVar",("Forces a physical field to be generated for properties that otherwise would not require one",[UsedOn TClassField])

+ 3 - 3
src/core/type.ml

@@ -83,7 +83,7 @@ and tconstant =
 	| TThis
 	| TSuper
 
-and tvar_extra = (type_params * texpr option) option
+and tvar_extra = (type_params * texpr * bool) option
 
 and tvar_origin =
 	| TVOLocalVariable
@@ -1526,8 +1526,8 @@ module Printer = struct
 			"a_write",s_opt (fun cf -> cf.cf_name) a.a_write;
 		]
 
-	let s_tvar_extra (tl,eo) =
-		Printf.sprintf "Some(%s, %s)" (s_type_params tl) (s_opt (s_expr_ast true "" s_type) eo)
+	let s_tvar_extra (tl,e,_) =
+		Printf.sprintf "Some(%s, %s)" (s_type_params tl) (s_expr_ast true "" s_type e)
 
 	let s_tvar v =
 		s_record_fields "" [

+ 6 - 6
src/filters/capturedVars.ml

@@ -128,8 +128,8 @@ let captured_vars com e =
 			let tmp_used = ref used in
 			let rec browse = function
 				| Block f | Loop f | Function f -> f browse
-				| Use ({ v_extra = Some( _ :: _, _) })
-				| Assign ({ v_extra = Some( _ :: _, _) }) when com.platform = Cs || com.platform = Java ->
+				| Use ({ v_extra = Some( _ :: _, _, _) })
+				| Assign ({ v_extra = Some( _ :: _, _, _) }) when com.platform = Cs || com.platform = Java ->
 					(* Java and C# deal with functions with type parameters in a different way *)
 					(* so they do should not be wrapped *)
 					()
@@ -219,8 +219,8 @@ let captured_vars com e =
 					incr depth;
 					f (collect_vars false);
 					decr depth;
-				| Use ({ v_extra = Some( _ :: _, _) })
-				| Assign ({ v_extra = Some( _ :: _, _) }) when com.platform = Cs || com.platform = Java ->
+				| Use ({ v_extra = Some( _ :: _, _, _) })
+				| Assign ({ v_extra = Some( _ :: _, _, _) }) when com.platform = Cs || com.platform = Java ->
 					(* Java/C# use a special handling for functions with type parmaters *)
 					()
 				| Declare v ->
@@ -256,8 +256,8 @@ let captured_vars com e =
 			decr depth;
 		| Declare v ->
 			vars := PMap.add v.v_id !depth !vars;
-		| Use ({ v_extra = Some( _ :: _, _) })
-		| Assign ({ v_extra = Some( _ :: _, _) }) when com.platform = Cs || com.platform = Java ->
+		| Use ({ v_extra = Some( _ :: _, _, _) })
+		| Assign ({ v_extra = Some( _ :: _, _, _) }) when com.platform = Cs || com.platform = Java ->
 			()
 		| Use v ->
 			(try

+ 1 - 1
src/generators/genjava.ml

@@ -2423,7 +2423,7 @@ let generate con =
 	TArrayTransform.configure gen (
 	fun e _ ->
 		match e.eexpr with
-			| TArray ({ eexpr = TLocal { v_extra = Some( _ :: _, _) } }, _) -> (* captured transformation *)
+			| TArray ({ eexpr = TLocal { v_extra = Some( _ :: _, _, _) } }, _) -> (* captured transformation *)
 				false
 			| TArray(e1, e2) ->
 				( match run_follow gen (follow e1.etype) with

+ 3 - 2
src/macro/macroApi.ml

@@ -1188,10 +1188,11 @@ and encode_tconst c =
 	encode_enum ITConstant tag pl
 
 and encode_tvar v =
-	let f_extra (pl,e) =
+	let f_extra (pl,e,inline) =
 		encode_obj OTVar_extra [
 			"params",encode_type_params pl;
-			"expr",vopt encode_texpr e
+			"expr",encode_texpr e;
+			"isInline",vbool inline;
 		]
 	in
 	encode_obj OTVar [

+ 1 - 1
src/optimization/analyzerTexpr.ml

@@ -712,7 +712,7 @@ module Fusion = struct
 			let num_writes = state#get_writes v in
 			let can_be_used_as_value = can_be_used_as_value com e in
 			let is_compiler_generated = match v.v_kind with VUser _ | VInlined -> false | _ -> true in
-			let has_type_params = match v.v_extra with Some (tl,_) when tl <> [] -> true | _ -> false in
+			let has_type_params = match v.v_extra with Some (tl,_,_) when tl <> [] -> true | _ -> false in
 			let b = num_uses <= 1 &&
 			        num_writes = 0 &&
 			        can_be_used_as_value &&

+ 2 - 2
src/optimization/analyzerTypes.ml

@@ -210,11 +210,11 @@ module Graph = struct
 		} in
 		DynArray.add g.g_var_infos vi;
 		let i = DynArray.length g.g_var_infos - 1 in
-		v.v_extra <- Some([],Some (mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos));
+		v.v_extra <- Some([],mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos,false);
 		vi
 
 	let get_var_info g v = match v.v_extra with
-		| Some(_,Some {eexpr = TConst (TInt i32)}) -> DynArray.get g.g_var_infos (Int32.to_int i32)
+		| Some(_,{eexpr = TConst (TInt i32)},_) -> DynArray.get g.g_var_infos (Int32.to_int i32)
 		| _ ->
 			print_endline "Unbound variable, please report this";
 			print_endline (Printer.s_tvar v);

+ 1 - 1
src/optimization/inline.ml

@@ -394,7 +394,7 @@ class inline_state ctx ethis params cf f p = object(self)
 					_had_side_effect <- true;
 					l.i_force_temp <- true;
 				end;
-				if l.i_abstract_this then l.i_subst.v_extra <- Some ([],Some e);
+				if l.i_abstract_this then l.i_subst.v_extra <- Some ([],e,true);
 				loop ((l,e) :: acc) pl al false
 			| [], (v,opt) :: al ->
 				let l = self#declare v in

+ 5 - 3
src/optimization/inlineConstructors.ml

@@ -98,7 +98,7 @@ let inline_constructors ctx e =
 			| IOKCtor(_,isextern,vars) ->
 				List.iter (fun v -> if v.v_id < 0 then cancel_v v p) vars;
 				if isextern then begin
-					display_error ctx "Extern constructor could not be inlined" io.io_pos;
+					display_error ctx "Forced inline constructor could not be inlined" io.io_pos;
 					display_error ctx "Cancellation happened here" p;
 				end
 			| _ -> ()
@@ -221,6 +221,7 @@ let inline_constructors ctx e =
 		in
 		match e.eexpr, e.etype with
 		| TNew({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,tl,pl),_
+		| TMeta((Meta.Inline,_,_),{eexpr = TNew({ cl_constructor = Some ({cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,tl,pl)}),_
 			when captured && not (List.memq cf seen_ctors) ->
 			begin
 				let io_id = !current_io_id in
@@ -245,7 +246,8 @@ let inline_constructors ctx e =
 				match Inline.type_inline_ctor ctx c cf tf (mk (TLocal v) (TInst (c,tl)) e.epos) pl e.epos with
 				| Some inlined_expr ->
 					let has_untyped = (Meta.has Meta.HasUntyped cf.cf_meta) in
-					let io = mk_io (IOKCtor(cf,is_extern_ctor c cf,argvs)) io_id inlined_expr ~has_untyped:has_untyped in
+					let forced = is_extern_ctor c cf || (match e.eexpr with TMeta _ -> true | _ -> false) in
+					let io = mk_io (IOKCtor(cf,forced,argvs)) io_id inlined_expr ~has_untyped:has_untyped in
 					let rec loop (c:tclass) (tl:t list) =
 						let apply = apply_params c.cl_params tl in
 						List.iter (fun cf ->
@@ -387,7 +389,7 @@ let inline_constructors ctx e =
 			([Type.map_expr f e], None)
 		in
 		match e.eexpr with
-		| TObjectDecl _ | TArrayDecl _ | TNew _ ->
+		| TObjectDecl _ | TArrayDecl _ | TNew _ | (TMeta((Meta.Inline,_,_),{eexpr = TNew _})) ->
 			begin try
 				let io = get_io !current_io_id in
 				if io.io_cancelled then begin

+ 7 - 1
src/syntax/grammar.mly

@@ -931,7 +931,12 @@ and parse_block_elt = parser
 		(EVars vl,punion p1 p2)
 	| [< '(Kwd Final,p1); vl = parse_var_decls true p1; p2 = semicolon >] ->
 		(EVars vl,punion p1 p2)
-	| [< '(Kwd Inline,p1); '(Kwd Function,_); e = parse_function p1 true; _ = semicolon >] -> e
+	| [< '(Kwd Inline,p1); s >] ->
+		begin match s with parser
+		| [< '(Kwd Function,_); e = parse_function p1 true; _ = semicolon >] -> e
+		| [< e = secure_expr; _ = semicolon >] -> make_meta Meta.Inline [] e p1
+		| [< >] -> serror()
+		end
 	| [< e = expr; _ = semicolon >] -> e
 
 and parse_obj_decl name e p0 s =
@@ -1253,6 +1258,7 @@ and expr = parser
 	| [< '(IntInterval i,p1); e2 = expr >] -> make_binop OpInterval (EConst (Int i),p1) e2
 	| [< '(Kwd Untyped,p1); e = secure_expr >] -> (EUntyped e,punion p1 (pos e))
 	| [< '(Dollar v,p); s >] -> expr_next (EConst (Ident ("$"^v)),p) s
+	| [< '(Kwd Inline,p); e = secure_expr >] -> make_meta Meta.Inline [] e p
 
 and expr_next e1 s =
 	try

+ 7 - 7
src/typing/calls.ml

@@ -15,7 +15,7 @@ let is_forced_inline c cf =
 	| _ when cf.cf_extern -> true
 	| _ -> false
 
-let make_call ctx e params t p =
+let make_call ctx e params t ?(force_inline=false) p =
 	try
 		let ethis,cl,f = match e.eexpr with
 			| TField (ethis,fa) ->
@@ -28,7 +28,7 @@ let make_call ctx e params t p =
 			| _ ->
 				raise Exit
 		in
-		if f.cf_kind <> Method MethInline then raise Exit;
+		if not force_inline && f.cf_kind <> Method MethInline then raise Exit;
 		let config = match cl with
 			| Some ({cl_kind = KAbstractImpl _}) when Meta.has Meta.Impl f.cf_meta ->
 				let t = if f.cf_name = "_new" then
@@ -234,9 +234,9 @@ let unify_field_call ctx fa el args ret p inline =
 	let attempt_call t cf = match follow t with
 		| TFun(args,ret) ->
 			let el,tf = unify_call_args' ctx el args ret p inline is_forced_inline in
-			let mk_call ethis p_field =
+			let mk_call ethis p_field inline =
 				let ef = mk (TField(ethis,mk_fa cf)) t p_field in
-				make_call ctx ef (List.map fst el) ret p
+				make_call ctx ef (List.map fst el) ret ~force_inline:inline p
 			in
 			el,tf,mk_call
 		| _ ->
@@ -269,7 +269,7 @@ let unify_field_call ctx fa el args ret p inline =
 	in
 	let fail_fun () =
 		let tf = TFun(args,ret) in
-		[],tf,(fun ethis p_field ->
+		[],tf,(fun ethis p_field _ ->
 			let e1 = mk (TField(ethis,mk_fa cf)) tf p_field in
 			mk (TCall(e1,[])) ret p)
 	in
@@ -519,7 +519,7 @@ let rec build_call ctx acc el (with_type:WithType.t) p =
 		(match follow t with
 			| TFun (args,r) ->
 				let _,_,mk_call = unify_field_call ctx fmode el args r p true in
-				mk_call ethis p
+				mk_call ethis p true
 			| _ ->
 				error (s_type (print_context()) t ^ " cannot be called") p
 		)
@@ -624,7 +624,7 @@ let rec build_call ctx acc el (with_type:WithType.t) p =
 							type_generic_function ctx (e1,fa) el with_type p
 						| _ ->
 							let _,_,mk_call = unify_field_call ctx fa el args r p false in
-							mk_call e1 e.epos
+							mk_call e1 e.epos false
 					end
 				| _ ->
 					let el, tfunc = unify_call_args ctx el args r p false false in

+ 39 - 15
src/typing/typer.ml

@@ -296,6 +296,14 @@ let unify_min ctx el =
 		if not ctx.untyped then display_error ctx (error_msg (Unify l)) p;
 		(List.hd el).etype
 
+let inline_local_function ctx v e params t p =
+	(* create a fake class with a fake field to emulate inlining *)
+	let c = mk_class ctx.m.curmod (["local"],v.v_name) e.epos null_pos in
+	let cf = { (mk_field v.v_name v.v_type e.epos null_pos) with cf_params = params; cf_expr = Some e; cf_kind = Method MethInline } in
+	c.cl_extern <- true;
+	c.cl_fields <- PMap.add cf.cf_name cf PMap.empty;
+	AKInline (mk (TConst TNull) (TInst (c,[])) p, cf, FInstance(c,[],cf), t)
+
 let rec type_ident_raise ctx i p mode =
 	match i with
 	| "true" ->
@@ -338,20 +346,14 @@ let rec type_ident_raise ctx i p mode =
 	try
 		let v = PMap.find i ctx.locals in
 		(match v.v_extra with
-		| Some (params,e) ->
+		| Some (params,e,inline) ->
 			let t = monomorphs params v.v_type in
-			(match e with
-			| Some ({ eexpr = TFunction f } as e) when ctx.com.display.dms_inline ->
+			(match e,inline with
+			| { eexpr = TFunction f },true when ctx.com.display.dms_inline ->
 				begin match mode with
 					| MSet -> error "Cannot set inline closure" p
 					| MGet -> error "Cannot create closure on inline closure" p
-					| MCall ->
-						(* create a fake class with a fake field to emulate inlining *)
-						let c = mk_class ctx.m.curmod (["local"],v.v_name) e.epos null_pos in
-						let cf = { (mk_field v.v_name v.v_type e.epos null_pos) with cf_params = params; cf_expr = Some e; cf_kind = Method MethInline } in
-						c.cl_extern <- true;
-						c.cl_fields <- PMap.add cf.cf_name cf PMap.empty;
-						AKInline (mk (TConst TNull) (TInst (c,[])) p, cf, FInstance(c,[],cf), t)
+					| MCall -> inline_local_function ctx v e params t p
 				end
 			| _ ->
 				AKExpr (mk (TLocal v) t p))
@@ -2031,7 +2033,7 @@ and type_local_function ctx name inline f with_type p =
 		| Some v ->
 			if starts_with v '$' then display_error ctx "Variable names starting with a dollar are not allowed" p;
 			let v = (add_local_with_origin ctx TVOLocalFunction v ft pname) in
-			if params <> [] then v.v_extra <- Some (params,None);
+			if params <> [] then v.v_extra <- Some (params,(mk (TConst TNull) t_dynamic null_pos),false);
 			Some v
 	) in
 	let curfun = match ctx.curfun with
@@ -2053,7 +2055,7 @@ and type_local_function ctx name inline f with_type p =
 	| Some v ->
 		Typeload.generate_value_meta ctx.com None (fun m -> v.v_meta <- m :: v.v_meta) f.f_args;
 		let open LocalUsage in
-		if params <> [] || inline then v.v_extra <- Some (params,if inline then Some e else None);
+		v.v_extra <- Some (params,e,inline);
 		let rec loop = function
 			| LocalUsage.Block f | LocalUsage.Loop f | LocalUsage.Function f -> f loop
 			| LocalUsage.Use v2 | LocalUsage.Assign v2 when v == v2 -> raise Exit
@@ -2269,19 +2271,41 @@ and type_meta ctx m e1 with_type p =
 			{e with eexpr = TMeta(m,e)}
 		| (Meta.Inline,_,_) ->
 			begin match fst e1 with
+			| ECall(e1,el) ->
+				type_call ctx e1 el WithType.value true p
+			| ENew (t,el) ->
+				let e = type_new ctx t el with_type p in
+				{e with eexpr = TMeta((Meta.Inline,[],null_pos),e)}
 			| EFunction(Some(_) as name,e1) ->
 				type_local_function ctx name true e1 with_type p
 			| _ ->
+				display_error ctx "Call or function expected after inline keyword" p;
 				e();
-			end;
+			end
 		| _ -> e()
 	in
 	ctx.meta <- old;
 	e
 
-and type_call ctx e el (with_type:WithType.t) p =
+and type_call ctx e el (with_type:WithType.t) inline p =
 	let def () =
 		let e = maybe_type_against_enum ctx (fun () -> type_access ctx (fst e) (snd e) MCall) with_type true p in
+		let e = if not inline then
+			e
+		else match e with
+			| AKExpr {eexpr = TField(e1,fa)} ->
+				begin match extract_field fa with
+				| Some cf ->
+					let t = monomorphs cf.cf_params cf.cf_type in
+					AKInline(e1,cf,fa,t)
+				| None ->
+					e
+				end;
+			| AKExpr {eexpr = TLocal ({v_extra = Some(params,({eexpr = TFunction _} as e1),_)} as v)} ->
+				let t = monomorphs params v.v_type in
+				inline_local_function ctx v e1 params t p
+			| _ -> e
+		in
 		let e = build_call ctx e el with_type p in
 		e
 	in
@@ -2456,7 +2480,7 @@ and type_expr ctx (e,p) (with_type:WithType.t) =
 		let e = type_expr ctx e WithType.value in
 		mk (TThrow e) (mk_mono()) p
 	| ECall (e,el) ->
-		type_call ctx e el with_type p
+		type_call ctx e el with_type false p
 	| ENew (t,el) ->
 		type_new ctx t el with_type p
 	| EUnop (op,flag,e) ->

+ 1 - 1
std/haxe/macro/Type.hx

@@ -753,7 +753,7 @@ typedef TVar = {
 		Special information which is internally used to keep track of closure.
 		information
 	**/
-	public var extra(default,never):Null<{params: Array<TypeParameter>, expr: Null<TypedExpr>}>;
+	public var extra(default,never):Null<{params: Array<TypeParameter>, expr: TypedExpr, isInline: Bool}>;
 
 	/**
 		The metadata of the variable.

+ 1 - 3
tests/misc/projects/Issue3678/compile-fail.hxml.stderr

@@ -1,3 +1 @@
-Main.hx:3: characters 18-19 : expression expected after =
-Main.hx:3: characters 20-70 : Unnamed lvalue functions are not supported
-Main.hx:4: characters 9-13 : Unknown identifier : func
+Main.hx:3: characters 20-70 : Call or function expected after inline keyword

+ 92 - 0
tests/optimization/src/issues/IssueInline.hx

@@ -0,0 +1,92 @@
+package issues;
+
+import TestJs.use;
+
+private class Point {
+	public var x:Int;
+	public var y:Int;
+
+	public function new(x:Int, y:Int) {
+		this.x = x;
+		this.y = y;
+	}
+}
+
+private class InlinePoint {
+	public var x:Int;
+	public var y:Int;
+
+	inline public function new(x:Int, y:Int) {
+		this.x = x;
+		this.y = y;
+	}
+}
+
+class IssueInline {
+	@:js('
+		TestJs.use(4);
+		TestJs.use(issues_IssueInline.testInline(3));
+		issues_IssueInline.testInline(3);
+	')
+	static function test() {
+		use(inline testInline(3));
+		inline testInline(3);
+		use(testInline(3));
+		testInline(3);
+	}
+
+	@:js('
+		var testInline = function(i) {return i + 1;};
+		TestJs.use(4);
+		TestJs.use(testInline(3));
+		testInline(3);
+	')
+	static function test2() {
+		function testInline(i:Int) {
+			return i + 1;
+		}
+		use(inline testInline(3));
+		inline testInline(3);
+		use(testInline(3));
+		testInline(3);
+	}
+
+	@:js('
+		TestJs.use(4);
+		TestJs.use(4);
+	')
+	static function test3() {
+		inline function testInline(i:Int) {
+			return i + 1;
+		}
+		use(inline testInline(3));
+		inline testInline(3);
+		use(testInline(3));
+		testInline(3);
+	}
+
+	@:js('
+		TestJs.use(1);
+		TestJs.use(2);
+	')
+	static function testCtor1() {
+		var x = inline new Point(1, 2);
+		use(x.x);
+		use(x.y);
+	}
+
+	@:js('
+		TestJs.use(1);
+		TestJs.use(2);
+	')
+	static function testCtor2() {
+		var x = inline new InlinePoint(1, 2);
+		use(x.x);
+		use(x.y);
+	}
+
+	@:pure(false)
+	static function testInline(i:Int) {
+		return i + 1;
+	}
+}