Skip to content

Commit db89ef2

Browse files
committed
Fix recursive untagged variant type checking
Fixes #7314 - Delay untagged variant well-formedness checks until after environment construction - Collect all untagged variant checks during type declaration processing - Perform checks once all recursive types are available in the environment - Add test case for valid recursive untagged variant type definitions This fixes issues where recursive references in untagged variants would fail validation due to premature checking before the full type environment was built.
1 parent e059e19 commit db89ef2

File tree

5 files changed

+36
-5
lines changed

5 files changed

+36
-5
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525

2626
- Deprecate JSON.Classify.classify. https://github.com/rescript-lang/rescript/pull/7315
2727

28+
#### :bug: Bug fix
29+
30+
- Fix recursive untagged variant type checking by delaying well-formedness checks until environment construction completes. [#7320](https://github.com/rescript-lang/rescript/pull/7320)
31+
2832
# 12.0.0-alpha.9
2933

3034
#### :boom: Breaking Change

compiler/ml/ast_untagged_variants.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,12 @@ let names_from_type_variant ?(is_untagged_def = false) ~env
377377
let blocks = Ext_array.reverse_of_list blocks in
378378
Some {consts; blocks}
379379

380-
let check_well_formed ~env ~is_untagged_def
381-
(cstrs : Types.constructor_declaration list) =
380+
type well_formedness_check = {
381+
is_untagged_def: bool;
382+
cstrs: Types.constructor_declaration list;
383+
}
384+
385+
let check_well_formed ~env {is_untagged_def; cstrs} =
382386
ignore (names_from_type_variant ~env ~is_untagged_def cstrs)
383387

384388
let has_undefined_literal attrs = process_tag_type attrs = Some Undefined

compiler/ml/typedecl.ml

+12-3
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ let make_constructor env type_path type_params sargs sret_type =
293293
any type variable present in [ty].
294294
*)
295295

296-
let transl_declaration ~type_record_as_object env sdecl id =
296+
let transl_declaration ~type_record_as_object ~untagged_wfc env sdecl id =
297297
(* Bind type parameters *)
298298
reset_type_variables ();
299299
Ctype.begin_def ();
@@ -529,7 +529,11 @@ let transl_declaration ~type_record_as_object env sdecl id =
529529
let is_untagged_def =
530530
Ast_untagged_variants.has_untagged sdecl.ptype_attributes
531531
in
532-
Ast_untagged_variants.check_well_formed ~env ~is_untagged_def cstrs;
532+
let well_formedness_check : Ast_untagged_variants.well_formedness_check =
533+
{is_untagged_def; cstrs}
534+
in
535+
(* delay the check until the newenv is created to handle recursive types *)
536+
untagged_wfc := well_formedness_check :: !untagged_wfc;
533537
(Ttype_variant tcstrs, Type_variant cstrs, sdecl)
534538
| Ptype_record lbls_ -> (
535539
let optional_labels =
@@ -1467,10 +1471,12 @@ let transl_type_decl env rec_flag sdecl_list =
14671471
| Asttypes.Recursive | Asttypes.Nonrecursive -> (id, None)
14681472
in
14691473
let type_record_as_object = ref false in
1474+
let untagged_wfc = ref [] in
14701475
let transl_declaration name_sdecl (id, slot) =
14711476
current_slot := slot;
14721477
Builtin_attributes.warning_scope name_sdecl.ptype_attributes (fun () ->
1473-
transl_declaration ~type_record_as_object temp_env name_sdecl id)
1478+
transl_declaration ~type_record_as_object ~untagged_wfc temp_env
1479+
name_sdecl id)
14741480
in
14751481
let tdecls =
14761482
List.map2 transl_declaration sdecl_list (List.map id_slots id_list)
@@ -1528,6 +1534,9 @@ let transl_type_decl env rec_flag sdecl_list =
15281534
| None -> ())
15291535
sdecl_list tdecls;
15301536
(* Check that constraints are enforced *)
1537+
List.iter
1538+
(fun check -> Ast_untagged_variants.check_well_formed ~env:newenv check)
1539+
!untagged_wfc;
15311540
List.iter2 (check_constraints ~type_record_as_object newenv) sdecl_list decls;
15321541
(* Name recursion *)
15331542
let decls =

tests/tests/src/UntaggedVariants.mjs

+7
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,12 @@ let ObjectAndNull = {
600600
printLength: printLength
601601
};
602602

603+
let RecursiveType = {
604+
o: {
605+
foo: "hello"
606+
}
607+
};
608+
603609
let $$Array;
604610

605611
let i = 42;
@@ -653,5 +659,6 @@ export {
653659
OnlyOne,
654660
MergeCases,
655661
ObjectAndNull,
662+
RecursiveType,
656663
}
657664
/* l2 Not a pure module */

tests/tests/src/UntaggedVariants.res

+7
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,10 @@ module ObjectAndNull = {
469469
| _ => ()
470470
}
471471
}
472+
473+
module RecursiveType = {
474+
type rec object2 = {foo: string}
475+
@unboxed and tagged2 = Object(object2) | Fn(unit => object2)
476+
477+
let o = Object({foo: "hello"})
478+
}

0 commit comments

Comments
 (0)