Skip to content
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
9 changes: 9 additions & 0 deletions lib/js_codegen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,15 @@ let gen_type_decl ctx (td : type_decl) : unit =
and `Type::Variant` references both work. Structs and aliases are erased. *)
match td.td_body with
| TyEnum variants ->
(* The runtime preamble already declares Some/None/Ok/Err; re-emitting
them for a program that declares `type Option`/`type Result` (e.g.
stdlib/prelude.affine) redeclares the same const (SyntaxError under
node). Skip the preamble-provided constructors. Mirrors the Deno-ESM
fix (#606). *)
let preamble_ctors = [ "Some"; "None"; "Ok"; "Err" ] in
List.iter (fun (vd : variant_decl) ->
if List.mem vd.vd_name.name preamble_ctors then ()
else begin
let name = mangle vd.vd_name.name in
let arity = List.length vd.vd_fields in
if arity = 0 then
Expand All @@ -550,6 +558,7 @@ let gen_type_decl ctx (td : type_decl) : unit =
(Printf.sprintf "const %s = (%s) => ({ tag: \"%s\", values: [%s] });"
name (String.concat ", " params)
vd.vd_name.name (String.concat ", " params))
end
) variants;
emit ctx "\n"
| TyStruct _ | TyAlias _ ->
Expand Down
45 changes: 42 additions & 3 deletions test/test_stdlib_aot.ml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ let pipeline_to_deno (prog : Ast.program) : (string, string) result =
| Error e -> Error (Printf.sprintf "deno-codegen: %s" e)
| Ok js -> Ok js)))

(** Same pipeline, JS (non-ESM) backend. Shares the Some/None/Ok/Err runtime
preamble with Deno-ESM, so it has the same duplicate-constructor surface. *)
let pipeline_to_js (prog : Ast.program) : (string, string) result =
let ld = loader () in
match Resolve.resolve_program_with_loader prog ld with
| Error (e, sp) ->
Error (Printf.sprintf "resolve: %s @ %s"
(Resolve.show_resolve_error e) (Span.show sp))
| Ok (rctx, itc) ->
(match
Typecheck.check_program
~import_types:itc.Typecheck.name_types rctx.symbols prog
with
| Error e ->
Error (Printf.sprintf "typecheck: %s" (Typecheck.format_type_error e))
| Ok _ ->
(match Borrow.check_program rctx.symbols prog with
| Error e ->
Error (Printf.sprintf "borrow: %s" (Borrow.format_borrow_error e))
| Ok () ->
let flat = Module_loader.flatten_imports ld prog in
(match Js_codegen.codegen_js flat rctx.symbols with
| Error e -> Error (Printf.sprintf "js-codegen: %s" e)
| Ok js -> Ok js)))

(** Full AOT pipeline to the core-Wasm backend: resolve -> typecheck ->
borrow -> [Codegen.generate_module] (loader-aware). Mirrors
[pipeline_to_deno] but targets the backend whose cross-module constructor
Expand Down Expand Up @@ -262,12 +287,26 @@ let test_deno_no_duplicate_option_ctor () =
"`const Some` declared exactly once (preamble only, not re-emitted)"
1 (count_substr "const Some" js))

let deno_dup_ctor_tests =
let test_js_no_duplicate_option_ctor () =
match Parse_driver.parse_string ~file:"<localopt>" local_option_src with
| exception e ->
Alcotest.failf "local-option parse raised: %s" (Printexc.to_string e)
| prog ->
(match pipeline_to_js prog with
| Error m -> Alcotest.failf "js codegen failed: %s" m
| Ok js ->
Alcotest.(check int)
"`const Some` declared exactly once (preamble only, not re-emitted)"
1 (count_substr "const Some" js))

let dup_ctor_tests =
[ Alcotest.test_case "declared Option does not duplicate preamble ctor (Deno)"
`Quick test_deno_no_duplicate_option_ctor ]
`Quick test_deno_no_duplicate_option_ctor;
Alcotest.test_case "declared Option does not duplicate preamble ctor (JS)"
`Quick test_js_no_duplicate_option_ctor ]

let tests =
[ ("STAGE-A AOT smoke (#136)", aot_smoke_tests);
("STAGE-A multi-module integration (#137)", integration_tests);
("cross-module constructor linking, Wasm (#138)", xmod_constructor_tests);
("Deno-ESM no duplicate Option/Result constructor", deno_dup_ctor_tests) ]
("Deno-ESM / JS no duplicate Option/Result constructor", dup_ctor_tests) ]
Loading