Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 8 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/compiler/hxb/hxbReader.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down
29 changes: 15 additions & 14 deletions src/compiler/hxb/hxbWriter.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -1059,31 +1059,31 @@ 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;
Chunk.write_uleb128 writer.chunk index
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
end else begin
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 *)

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
27 changes: 27 additions & 0 deletions tests/server/src/cases/issues/Issue11589.hx
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions tests/server/test/templates/issues/Issue11589/Main.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function main() {}

typedef Foo<T> = {} & { foo:T }
8 changes: 8 additions & 0 deletions tests/server/test/templates/issues/Issue11589/Main1.hx
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions tests/server/test/templates/issues/Issue11589/Main2.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
function main() {
foo("");
}

function foo<T>(v:T):Foo<T> return {foo:{bar:v}}
typedef Foo<T> = { foo : { bar : T } }