Bladeren bron

Deal with locals static inits properly (#11999)

* start dealing with static inits properly

see #11919

* maybe PHP needs this too

* and Flash as well

* fix
Simon Krajewski 10 maanden geleden
bovenliggende
commit
cd7ba8c0f5
2 gewijzigde bestanden met toevoegingen van 96 en 5 verwijderingen
  1. 35 5
      src/filters/localStatic.ml
  2. 61 0
      tests/unit/src/unit/issues/Issue11919.hx

+ 35 - 5
src/filters/localStatic.ml

@@ -27,19 +27,49 @@ let promote_local_static lsctx run v eo =
 			let no_local_in_static p =
 				raise_typing_error "Accessing local variables in static initialization is not allowed" p
 			in
-			let rec loop e = match e.eexpr with
+			let declared_vars = Hashtbl.create 0 in
+			let declare v = Hashtbl.add declared_vars v.v_id () in
+			let rec loop in_function in_loop e =
+				let loop' = loop in_function in_loop in
+				match e.eexpr with
 				| TLocal v when has_var_flag v VStatic ->
 					run e
-				| TFunction _ | TLocal _ ->
+				| TLocal v when not (Hashtbl.mem declared_vars v.v_id) ->
 					no_local_in_static e.epos
+				| TVar(v,eo) ->
+					let eo = Option.map loop' eo in
+					declare v;
+					{e with eexpr = TVar(v,eo)}
+				| TFunction tf ->
+					let args = List.map (fun (v,eo) ->
+						declare v;
+						let eo = Option.map loop' eo in
+						(v,eo)
+					) tf.tf_args in
+					let e1 = loop true in_loop tf.tf_expr in
+					{e with eexpr = TFunction {tf with tf_args = args;tf_expr = e1}}
+				| TTry(e1,catches) ->
+					let e1 = loop' e1 in
+					let catches = List.map (fun (v,e) ->
+						declare v;
+						let e = loop' e in
+						(v,e)
+					) catches in
+					{e with eexpr = TTry(e1,catches)}
+				| TWhile(e1,e2,flag) ->
+					let e1 = loop' e1 in
+					let e2 = loop in_function true e2 in
+					{e with eexpr = TWhile(e1,e2,flag)}
 				| TConst (TThis | TSuper) ->
 					raise_typing_error "Accessing `this` in static initialization is not allowed" e.epos
-				| TReturn _ | TBreak | TContinue ->
+				| TReturn _ when not in_function ->
+					raise_typing_error "This kind of control flow in static initialization is not allowed" e.epos
+				| TBreak | TContinue when not in_loop ->
 					raise_typing_error "This kind of control flow in static initialization is not allowed" e.epos
 				| _ ->
-					map_expr loop e
+					map_expr loop' e
 			in
-			let e = loop e in
+			let e = loop false false e in
 			cf.cf_expr <- Some e
 		end;
 		lsctx.added_fields <- cf :: lsctx.added_fields;

+ 61 - 0
tests/unit/src/unit/issues/Issue11919.hx

@@ -0,0 +1,61 @@
+package unit.issues;
+
+import unit.Test;
+
+private class Func {
+	public var f:Int->Int;
+
+	public function new(f:Int->Int) {
+		this.f = f;
+	}
+}
+
+class Issue11919 extends Test {
+	static function getInt() {
+		return 5;
+	}
+
+	function test() {
+		static var localFunction = new Func(a -> a);
+		eq(2, localFunction.f(2));
+
+		static var declaredLocal = {
+			var f = getInt();
+			f;
+		}
+		eq(5, declaredLocal);
+
+		static var caughtVar = {
+			try {
+				throw "foo";
+			} catch (s:String) {
+				s;
+			}
+		}
+		eq("foo", caughtVar);
+
+		static var loopBreak = {
+			var acc = 0;
+			for (i in 0...getInt()) {
+				acc += i;
+				if (i == 2) {
+					break;
+				}
+			}
+			acc;
+		}
+		eq(3, loopBreak);
+
+		static var loopContinue = {
+			var acc = 0;
+			for (i in 0...getInt()) {
+				if (i & 1 == 0) {
+					continue;
+				}
+				acc += i;
+			}
+			acc;
+		}
+		eq(4, loopContinue);
+	}
+}