Browse Source

[hxb] detect need for local context in nested anons (#11617)

* [hxb] detect need for local context in nested anons

* [hxb] add compiler failure message

* [tests] add tests for 11589

* [tests] add original Hide issue too

* [hxb] handle another edge case
Rudy Ges 1 year ago
parent
commit
ea964ad99e

+ 16 - 1
src/compiler/hxb/hxbReader.ml

@@ -1973,11 +1973,26 @@ class hxb_reader
 		| EOM ->
 			incr stats.modules_fully_restored;
 
+	method private die chunk msg =
+		let msg =
+			(Printf.sprintf "Compiler failure while reading hxb chunk %s of %s: %s\n" (string_of_chunk_kind chunk) (s_type_path mpath) (msg))
+			^ "Please submit an issue at https://github.com/HaxeFoundation/haxe/issues/new\n"
+			^ "Attach the following information:"
+		in
+		let backtrace = Printexc.raw_backtrace_to_string (Printexc.get_raw_backtrace ()) in
+		let s = Printf.sprintf "%s\nHaxe: %s\n%s" msg s_version_full backtrace in
+		failwith s
+
 	method private read_chunk_data kind =
 		let path = String.concat "_" (ExtLib.String.nsplit (s_type_path mpath) ".") in
 		let id = ["hxb";"read";string_of_chunk_kind kind;path] in
 		let close = Timer.timer id in
-		self#read_chunk_data' kind;
+		try
+			self#read_chunk_data' kind
+		with Invalid_argument msg -> begin
+			close();
+			self#die kind msg
+		end;
 		close()
 
 	method read_chunks (new_api : hxb_reader_api) (chunks : cached_chunks) =

+ 15 - 14
src/compiler/hxb/hxbWriter.ml

@@ -469,6 +469,7 @@ type hxb_writer = {
 	mutable local_type_parameters : (typed_type_param,unit) IdentityPool.t;
 	mutable field_stack : unit list;
 	mutable wrote_local_type_param : bool;
+	mutable needs_local_context : bool;
 	unbound_ttp : (typed_type_param,unit) IdentityPool.t;
 	t_instance_chunk : Chunk.t;
 }
@@ -1006,12 +1007,11 @@ module HxbWriter = struct
 		write_pos writer v.v_pos
 
 	let rec write_anon writer (an : tanon) =
-		let needs_local_context = ref false in
 		let write_fields () =
 			let restore = start_temporary_chunk writer 256 in
 			let i = ref 0 in
 			PMap.iter (fun _ cf ->
-				write_anon_field_ref writer needs_local_context cf;
+				write_anon_field_ref writer cf;
 				incr i;
 			) an.a_fields;
 			let bytes = restore (fun new_chunk -> Chunk.get_bytes new_chunk) in
@@ -1035,8 +1035,7 @@ module HxbWriter = struct
 			assert false
 		| AbstractStatics _ ->
 			assert false
-		end;
-		!needs_local_context
+		end
 
 	and write_anon_ref writer (an : tanon) =
 		let pfm = Option.get (writer.anon_id#identify_anon ~strict:true an) in
@@ -1046,9 +1045,10 @@ module HxbWriter = struct
 			Chunk.write_uleb128 writer.chunk index
 		with Not_found ->
 			let restore = start_temporary_chunk writer 256 in
-			let needs_local_context = write_anon writer an in
+			writer.needs_local_context <- false;
+			write_anon writer an;
 			let bytes = restore (fun new_chunk -> Chunk.get_bytes new_chunk) in
-			if needs_local_context then begin
+			if writer.needs_local_context then begin
 				let index = Pool.add writer.anons pfm.pfm_path None in
 				Chunk.write_u8 writer.chunk 1;
 				Chunk.write_uleb128 writer.chunk index;
@@ -1059,7 +1059,7 @@ module HxbWriter = struct
 				Chunk.write_uleb128 writer.chunk index;
 			end
 
-	and write_anon_field_ref writer needs_local_context cf =
+	and write_anon_field_ref writer cf =
 		try
 			let index = HashedIdentityPool.get writer.anon_fields cf.cf_name cf in
 			Chunk.write_u8 writer.chunk 0;
@@ -1067,15 +1067,14 @@ module HxbWriter = struct
 		with Not_found ->
 			let restore = start_temporary_chunk writer 256 in
 			let old = writer.wrote_local_type_param in
+			writer.wrote_local_type_param <- false;
 			ignore(write_class_field_and_overloads_data writer true cf);
-			let wrote_local_type_param = writer.wrote_local_type_param in
-			writer.wrote_local_type_param <- old;
 			let bytes = restore (fun new_chunk -> Chunk.get_bytes new_chunk) in
-			if wrote_local_type_param then begin
+			if writer.needs_local_context || writer.wrote_local_type_param then begin
 				(* If we access something from the method scope, we have to write the anon field immediately.
 				   This should be fine because in such cases the field cannot be referenced elsewhere. *)
 				let index = HashedIdentityPool.add writer.anon_fields cf.cf_name cf None in
-				needs_local_context := true;
+				writer.needs_local_context <- true;
 				Chunk.write_u8 writer.chunk 1;
 				Chunk.write_uleb128 writer.chunk index;
 				Chunk.write_bytes writer.chunk bytes
@@ -1083,7 +1082,8 @@ module HxbWriter = struct
 				let index = HashedIdentityPool.add writer.anon_fields cf.cf_name cf (Some bytes) in
 				Chunk.write_u8 writer.chunk 0;
 				Chunk.write_uleb128 writer.chunk index;
-			end
+			end;
+			writer.wrote_local_type_param <- old
 
 	(* Type instances *)
 
@@ -1565,7 +1565,7 @@ module HxbWriter = struct
 			| TField(e1,FAnon cf) ->
 				Chunk.write_u8 writer.chunk 104;
 				loop e1;
-				write_anon_field_ref writer (ref false) cf;
+				write_anon_field_ref writer cf;
 				true;
 			| TField(e1,FClosure(Some(c,tl),cf)) ->
 				Chunk.write_u8 writer.chunk 105;
@@ -1577,7 +1577,7 @@ module HxbWriter = struct
 			| TField(e1,FClosure(None,cf)) ->
 				Chunk.write_u8 writer.chunk 106;
 				loop e1;
-				write_anon_field_ref writer (ref false) cf;
+				write_anon_field_ref writer cf;
 				true;
 			| TField(e1,FEnum(en,ef)) ->
 				Chunk.write_u8 writer.chunk 107;
@@ -2312,6 +2312,7 @@ let create config warn anon_id =
 		local_type_parameters = IdentityPool.create ();
 		field_stack = [];
 		wrote_local_type_param = false;
+		needs_local_context = false;
 		unbound_ttp = IdentityPool.create ();
 		t_instance_chunk = Chunk.create EOM cp 32;
 	}

+ 27 - 0
tests/server/src/cases/issues/Issue11589.hx

@@ -0,0 +1,27 @@
+package cases.issues;
+
+class Issue11589 extends TestCase {
+	function test(_) {
+		vfs.putContent("Main.hx", getTemplate("issues/Issue11589/Main.hx"));
+		var args = ["--main", "Main.hx", "--no-output"];
+		runHaxe(args);
+		runHaxe(args);
+		Assert.isFalse(lastResult.hasError);
+	}
+
+	function testNestedField(_) {
+		vfs.putContent("Main.hx", getTemplate("issues/Issue11589/Main1.hx"));
+		var args = ["--main", "Main.hx", "--no-output"];
+		runHaxe(args);
+		runHaxe(args);
+		Assert.isFalse(lastResult.hasError);
+	}
+
+	function testNestedFieldUsed(_) {
+		vfs.putContent("Main.hx", getTemplate("issues/Issue11589/Main2.hx"));
+		var args = ["--main", "Main.hx", "--no-output"];
+		runHaxe(args);
+		runHaxe(args);
+		Assert.isFalse(lastResult.hasError);
+	}
+}

+ 3 - 0
tests/server/test/templates/issues/Issue11589/Main.hx

@@ -0,0 +1,3 @@
+function main() {}
+
+typedef Foo<T> = {} & { foo:T }

+ 8 - 0
tests/server/test/templates/issues/Issue11589/Main1.hx

@@ -0,0 +1,8 @@
+function main() {}
+
+typedef Foo<T> = { foo : { bar : T } }
+
+typedef Bar<T> = {
+	function foo( elements : Array<{ value : T }> ) : Void;
+	function bar( foo : T, bar : { baz : Bool } ) : Void;
+}

+ 6 - 0
tests/server/test/templates/issues/Issue11589/Main2.hx

@@ -0,0 +1,6 @@
+function main() {
+	foo("");
+}
+
+function foo<T>(v:T):Foo<T> return {foo:{bar:v}}
+typedef Foo<T> = { foo : { bar : T } }