Przeglądaj źródła

Support `static` for locals (#10555)

* get the basics down

* dodge test problems via publicity

* error a lot
Simon Krajewski 3 lat temu
rodzic
commit
16edbca2c8
35 zmienionych plików z 201 dodań i 14 usunięć
  1. 3 1
      src/core/ast.ml
  2. 1 0
      src/core/tType.ml
  3. 60 0
      src/filters/filters.ml
  4. 4 1
      src/macro/macroApi.ml
  5. 14 3
      src/syntax/grammar.mly
  6. 1 0
      src/syntax/reification.ml
  7. 4 1
      src/typing/typer.ml
  8. 5 0
      std/haxe/macro/Expr.hx
  9. 8 8
      tests/display/src/cases/StructureCompletion.hx
  10. 7 0
      tests/misc/projects/local-static/Break/Main.hx
  11. 2 0
      tests/misc/projects/local-static/Break/compile-fail.hxml
  12. 1 0
      tests/misc/projects/local-static/Break/compile-fail.hxml.stderr
  13. 7 0
      tests/misc/projects/local-static/Continue/Main.hx
  14. 2 0
      tests/misc/projects/local-static/Continue/compile-fail.hxml
  15. 1 0
      tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr
  16. 6 0
      tests/misc/projects/local-static/Function/Main.hx
  17. 2 0
      tests/misc/projects/local-static/Function/compile-fail.hxml
  18. 1 0
      tests/misc/projects/local-static/Function/compile-fail.hxml.stderr
  19. 6 0
      tests/misc/projects/local-static/Local/Main.hx
  20. 2 0
      tests/misc/projects/local-static/Local/compile-fail.hxml
  21. 1 0
      tests/misc/projects/local-static/Local/compile-fail.hxml.stderr
  22. 6 0
      tests/misc/projects/local-static/LocalVsLocal/Main.hx
  23. 2 0
      tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml
  24. 2 0
      tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr
  25. 6 0
      tests/misc/projects/local-static/LocalVsStatic/Main.hx
  26. 2 0
      tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml
  27. 2 0
      tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr
  28. 5 0
      tests/misc/projects/local-static/Return/Main.hx
  29. 2 0
      tests/misc/projects/local-static/Return/compile-fail.hxml
  30. 1 0
      tests/misc/projects/local-static/Return/compile-fail.hxml.stderr
  31. 11 0
      tests/misc/projects/local-static/This/Main.hx
  32. 2 0
      tests/misc/projects/local-static/This/compile-fail.hxml
  33. 1 0
      tests/misc/projects/local-static/This/compile-fail.hxml.stderr
  34. 20 0
      tests/unit/src/unit/TestLocalStatic.hx
  35. 1 0
      tests/unit/src/unit/TestMain.hx

+ 3 - 1
src/core/ast.ml

@@ -314,6 +314,7 @@ and class_field = {
 and evar = {
 	ev_name : placed_name;
 	ev_final : bool;
+	ev_static : bool;
 	ev_type : type_hint option;
 	ev_expr : expr option;
 	ev_meta : metadata;
@@ -387,10 +388,11 @@ let mk_type_path ?(params=[]) ?sub (pack,name) =
 		raise (Invalid_argument "Empty module name is not allowed");
 	{ tpackage = pack; tname = name; tsub = sub; tparams = params; }
 
-let mk_evar ?(final=false) ?(t:type_hint option) ?eo ?(meta=[]) name =
+let mk_evar ?(final=false) ?(static=false) ?(t:type_hint option) ?eo ?(meta=[]) name =
 	{
 		ev_name = name;
 		ev_final = final;
+		ev_static = static;
 		ev_type = t;
 		ev_expr = eo;
 		ev_meta = meta;

+ 1 - 0
src/core/tType.ml

@@ -425,3 +425,4 @@ type flag_tvar =
 	| VUsed (* used by the analyzer *)
 	| VAssigned
 	| VCaught
+	| VStatic

+ 60 - 0
src/filters/filters.ml

@@ -66,6 +66,65 @@ let rec add_final_return e =
 			{ e with eexpr = TFunction f }
 		| _ -> e
 
+module LocalStatic = struct
+	let promote_local_static ctx lut v eo =
+		let name = Printf.sprintf "%s_%s" ctx.curfield.cf_name v.v_name in
+		begin try
+			let cf = PMap.find name ctx.curclass.cl_statics in
+			display_error ctx (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos;
+			typing_error "Conflicting field was found here" cf.cf_name_pos;
+		with Not_found ->
+			let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in
+			begin match eo with
+			| None ->
+				()
+			| Some e ->
+				let rec loop e = match e.eexpr with
+					| TLocal _ | TFunction _ ->
+						typing_error "Accessing local variables in static initialization is not allowed" e.epos
+					| TConst (TThis | TSuper) ->
+						typing_error "Accessing `this` in static initialization is not allowed" e.epos
+					| TReturn _ | TBreak | TContinue ->
+						typing_error "This kind of control flow in static initialization is not allowed" e.epos
+					| _ ->
+						iter loop e
+				in
+				loop e;
+				cf.cf_expr <- Some e
+			end;
+			TClass.add_field ctx.curclass cf;
+			Hashtbl.add lut v.v_id cf
+		end
+
+	let find_local_static lut v =
+		Hashtbl.find lut v.v_id
+
+	let run ctx e =
+		let local_static_lut = Hashtbl.create 0 in
+		let c = ctx.curclass in
+		let rec run e = match e.eexpr with
+			| TBlock el ->
+				let el = ExtList.List.filter_map (fun e -> match e.eexpr with
+					| TVar(v,eo) when has_var_flag v VStatic ->
+						promote_local_static ctx local_static_lut v eo;
+						None
+					| _ ->
+						Some (run e)
+				) el in
+				{ e with eexpr = TBlock el }
+			| TLocal v when has_var_flag v VStatic ->
+				begin try
+					let cf = find_local_static local_static_lut v in
+					Texpr.Builder.make_static_field c cf e.epos
+				with Not_found ->
+					typing_error (Printf.sprintf "Could not find local static %s (id %i)" v.v_name v.v_id) e.epos
+				end
+			| _ ->
+				Type.map_expr run e
+		in
+		run e
+end
+
 (* -------------------------------------------------------------------------- *)
 (* CHECK LOCAL VARS INIT *)
 
@@ -745,6 +804,7 @@ let run com tctx main =
 	] in
 	List.iter (run_expression_filters (timer_label detail_times ["expr 0"]) tctx filters) new_types;
 	let filters = [
+		"local_statics",LocalStatic.run tctx;
 		"fix_return_dynamic_from_void_function",fix_return_dynamic_from_void_function tctx true;
 		"check_local_vars_init",check_local_vars_init tctx.com;
 		"check_abstract_as_value",check_abstract_as_value;

+ 4 - 1
src/macro/macroApi.ml

@@ -447,6 +447,7 @@ and encode_expr e =
 						"name",encode_placed_name v.ev_name;
 						"name_pos",encode_pos (pos v.ev_name);
 						"isFinal",vbool v.ev_final;
+						"isStatic",vbool v.ev_static;
 						"type",null encode_ctype v.ev_type;
 						"expr",null loop v.ev_expr;
 						"meta",encode_meta_content v.ev_meta;
@@ -794,12 +795,14 @@ and decode_expr v =
 			EVars (List.map (fun v ->
 				let vfinal = field v "isFinal" in
 				let final = if vfinal == vnull then false else decode_bool vfinal in
+				let vstatic = field v "isStatic" in
+				let static = if vstatic == vnull then false else decode_bool vstatic in
 				let vmeta = field v "meta" in
 				let meta = if vmeta == vnull then [] else decode_meta_content vmeta in
 				let name = (decode_placed_name (field v "name_pos") (field v "name"))
 				and t = opt decode_ctype (field v "type")
 				and eo = opt loop (field v "expr") in
-				mk_evar ~final ?t ?eo ~meta name
+				mk_evar ~final ~static ?t ?eo ~meta name
 			) (decode_array vl))
 		| 11, [kind;f] ->
 			EFunction (decode_function_kind kind,decode_fun f)

+ 14 - 3
src/syntax/grammar.mly

@@ -1109,17 +1109,28 @@ and block_with_pos' acc f p s =
 and block_with_pos acc p s =
 	block_with_pos' acc parse_block_elt p s
 
-and parse_block_elt = parser
+and parse_block_var = parser
 	| [< '(Kwd Var,p1); vl = parse_var_decls false p1; p2 = semicolon >] ->
-		(EVars vl,punion p1 p2)
+		(vl,punion p1 p2)
 	| [< '(Kwd Final,p1); vl = parse_var_decls true p1; p2 = semicolon >] ->
-		(EVars vl,punion p1 p2)
+		(vl,punion p1 p2)
+
+and parse_block_elt = parser
+	| [< (vl,p) = parse_block_var >] ->
+		(EVars vl,p)
 	| [< '(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
+	| [< '(Kwd Static,p); s >] ->
+		begin match s with parser
+		| [< (vl,p) = parse_block_var >] ->
+			let vl = List.map (fun ev -> {ev with ev_static = true}) vl in
+			(EVars vl,p)
+		| [<>] -> syntax_error (Expected ["var";"final"]) s (mk_null_expr p)
+		end
 	| [< '(Binop OpLt,p1); s >] ->
 		let e = handle_xml_literal p1 in
 		(* accept but don't expect semicolon *)

+ 1 - 0
src/syntax/reification.ml

@@ -290,6 +290,7 @@ let reify in_macro =
 					"type", to_opt to_type_hint v.ev_type p;
 					"expr", to_opt to_expr v.ev_expr p;
 					"isFinal",to_bool v.ev_final p;
+					"isStatic",to_bool v.ev_static p;
 					"meta",to_meta v.ev_meta p;
 				] in
 				to_obj fields p

+ 4 - 1
src/typing/typer.ml

@@ -618,7 +618,9 @@ and type_vars ctx vl p =
 			let e = (match ev.ev_expr with
 				| None -> None
 				| Some e ->
-					let e = type_expr ctx e (WithType.with_type t) in
+					let old_in_loop = ctx.in_loop in
+					if ev.ev_static then ctx.in_loop <- false;
+					let e = Std.finally (fun () -> ctx.in_loop <- old_in_loop) (type_expr ctx e) (WithType.with_type t) in
 					let e = AbstractCast.cast_or_unify ctx t e p in
 					Some e
 			) in
@@ -626,6 +628,7 @@ and type_vars ctx vl p =
 			v.v_meta <- ev.ev_meta;
 			DisplayEmitter.check_display_metadata ctx v.v_meta;
 			if ev.ev_final then add_var_flag v VFinal;
+			if ev.ev_static then add_var_flag v VStatic;
 			if ctx.in_display && DisplayPosition.display_position#enclosed_in pv then
 				DisplayEmitter.display_variable ctx v pv;
 			v,e

+ 5 - 0
std/haxe/macro/Expr.hx

@@ -321,6 +321,11 @@ typedef Var = {
 	**/
 	var ?isFinal:Bool;
 
+	/**
+		Whether or not the variable is static.
+	**/
+	var ?isStatic:Bool;
+
 	/**
 		Metadata associatied with the variable, if available.
 	**/

+ 8 - 8
tests/display/src/cases/StructureCompletion.hx

@@ -32,7 +32,7 @@ class StructureCompletion extends DisplayTestCase {
 				test({{-1-}
 			}
 
-			static function test(o:{a:Float, b:String}) { }
+			public static function test(o:{a:Float, b:String}) { }
 		}
 	**/
 	function testStructureCompletion4() {
@@ -46,7 +46,7 @@ class StructureCompletion extends DisplayTestCase {
 				test(0, {{-1-}
 			}
 
-			static function test(x, o:{a:Float, b:String}) { }
+			public static function test(x, o:{a:Float, b:String}) { }
 		}
 	**/
 	function testStructureCompletion5() {
@@ -168,12 +168,12 @@ class StructureCompletion extends DisplayTestCase {
 			var field2:String;
 		}
 		class Main {
-			static function test1():Foo return { f{-1-}ie{-2-}
-			static function test2():Foo return { f{-3-}ie{-4-}:
-			static function test3():Foo return { f{-5-}ie{-6-} }
-			static function test4():Foo return { f{-7-}ie{-8-} : }
-			static function test5():Foo return { f{-9-}ie{-10-} : null }
-			static function test6():Foo return { f{-11-}ie{-12-} : null
+			public static function test1():Foo return { f{-1-}ie{-2-}
+			public static function test2():Foo return { f{-3-}ie{-4-}:
+			public static function test3():Foo return { f{-5-}ie{-6-} }
+			public static function test4():Foo return { f{-7-}ie{-8-} : }
+			public static function test5():Foo return { f{-9-}ie{-10-} : null }
+			public static function test6():Foo return { f{-11-}ie{-12-} : null
 	**/
 	function testStructureVsToplevel9() {
 		for (i in 1...13) {

+ 7 - 0
tests/misc/projects/local-static/Break/Main.hx

@@ -0,0 +1,7 @@
+class Main {
+	static function main() {
+		while(Math.random() > 0.5) {
+			static var x = break;
+		}
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/Break/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/Break/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:4: characters 19-24 : Break outside loop

+ 7 - 0
tests/misc/projects/local-static/Continue/Main.hx

@@ -0,0 +1,7 @@
+class Main {
+	static function main() {
+		while(Math.random() > 0.5) {
+			static var x = continue;
+		}
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/Continue/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:4: characters 19-27 : Continue outside loop

+ 6 - 0
tests/misc/projects/local-static/Function/Main.hx

@@ -0,0 +1,6 @@
+class Main {
+	static function main() {
+		function x() {}
+		static var y = x;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/Function/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/Function/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:4: characters 18-19 : Accessing local variables in static initialization is not allowed

+ 6 - 0
tests/misc/projects/local-static/Local/Main.hx

@@ -0,0 +1,6 @@
+class Main {
+	static function main() {
+		var x = 1;
+		static var y = x;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/Local/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/Local/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:4: characters 18-19 : Accessing local variables in static initialization is not allowed

+ 6 - 0
tests/misc/projects/local-static/LocalVsLocal/Main.hx

@@ -0,0 +1,6 @@
+class Main {
+	static function main() {
+		static var x = 1;
+		static var x = 2;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 2 - 0
tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:4: characters 14-15 : The expanded name of this local (main_x) conflicts with another static field
+Main.hx:3: characters 14-15 : Conflicting field was found here

+ 6 - 0
tests/misc/projects/local-static/LocalVsStatic/Main.hx

@@ -0,0 +1,6 @@
+class Main {
+	static var main_x = 1;
+	static function main() {
+		static var x = 2;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 2 - 0
tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:4: characters 14-15 : The expanded name of this local (main_x) conflicts with another static field
+Main.hx:2: characters 13-19 : Conflicting field was found here

+ 5 - 0
tests/misc/projects/local-static/Return/Main.hx

@@ -0,0 +1,5 @@
+class Main {
+	static function main() {
+		static var x = return;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/Return/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/Return/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:3: characters 18-24 : This kind of control flow in static initialization is not allowed

+ 11 - 0
tests/misc/projects/local-static/This/Main.hx

@@ -0,0 +1,11 @@
+class Main {
+	static function main() {
+
+	}
+
+	var x = 1;
+
+	function test() {
+		static var y = x;
+	}
+}

+ 2 - 0
tests/misc/projects/local-static/This/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 1 - 0
tests/misc/projects/local-static/This/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:9: characters 18-19 : Accessing `this` in static initialization is not allowed

+ 20 - 0
tests/unit/src/unit/TestLocalStatic.hx

@@ -0,0 +1,20 @@
+package unit;
+
+class TestLocalStatic extends Test {
+	function basic() {
+		static var x = 1;
+		static final y = "final";
+		x++;
+		return {x: x, y: y};
+	}
+
+	function testBasic() {
+		var obj = basic();
+		eq(2, obj.x);
+		eq("final", obj.y);
+
+		obj = basic();
+		eq(3, obj.x);
+		eq("final", obj.y);
+	}
+}

+ 1 - 0
tests/unit/src/unit/TestMain.hx

@@ -56,6 +56,7 @@ function main() {
 		new TestBytes(),
 		new TestIO(),
 		new TestLocals(),
+		new TestLocalStatic(),
 		new TestEReg(),
 		new TestXML(),
 		new TestMisc(),