Skip to content

Commit 6e6fe0a

Browse files
authored
Improve a few error messages around polymorphic variants (#7596)
* improve a few error messages around polymorphic variants * add note * changelog * wording
1 parent cfa195a commit 6e6fe0a

10 files changed

+125
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
- Better error message for when trying to await something that is not a promise. https://github.com/rescript-lang/rescript/pull/7561
5151
- Better error messages for object field missing and object field type mismatches. https://github.com/rescript-lang/rescript/pull/7580
52+
- Better error messages for when polymorphic variants does not match for various reasons. https://github.com/rescript-lang/rescript/pull/7596
5253

5354
#### :house: Internal
5455

compiler/ml/error_message_utils.ml

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,22 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
493493
@{<info>?%s@}"
494494
(Parser.extract_text_at_loc loc)
495495
| _, Some (t1, t2) ->
496-
let is_subtype =
497-
try
498-
Ctype.subtype env t1 t2 ();
499-
true
500-
with _ -> false
496+
let can_show_coercion_message =
497+
match (t1.desc, t2.desc) with
498+
| Tvariant _, Tvariant _ ->
499+
(* Subtyping polymorphic variants give some weird messages sometimes,
500+
so let's turn it off for now. For an example, turn them on again and try:
501+
```
502+
let a: [#Resize | #KeyDown] = #Resize
503+
let b: [#Click] = a
504+
```
505+
*)
506+
false
507+
| _ -> (
508+
try
509+
Ctype.subtype env t1 t2 ();
510+
true
511+
with _ -> false)
501512
in
502513
let target_type_string = Format.asprintf "%a" type_expr t2 in
503514
let target_expr_text = Parser.extract_text_at_loc loc in
@@ -519,7 +530,7 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
519530
| _ -> false
520531
in
521532
522-
if is_subtype && not is_constant then (
533+
if can_show_coercion_message && not is_constant then (
523534
fprintf ppf
524535
"@,\
525536
@,\

compiler/ml/printtyp.ml

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,17 +1385,54 @@ let explanation unif t3 t4 ppf =
13851385
(row1.row_fields, row1.row_closed, row2.row_fields, row2.row_closed)
13861386
with
13871387
| [], true, [], true ->
1388-
fprintf ppf "@,These two variant types have no intersection"
1388+
fprintf ppf
1389+
"@,\
1390+
@,\
1391+
These polymorphic variants are incompatible - they share no common \
1392+
constructors."
13891393
| [], true, (_ :: _ as fields), _ ->
1394+
(* TODO(ai) Future opportunity to provide a way for an LLM to lookup the
1395+
full polyvariant type definitions if wanted.*)
1396+
let constructors_txt =
1397+
if List.length fields = 1 then "constructor" else "constructors"
1398+
in
13901399
fprintf ppf
1391-
"@,@[The first variant type does not allow tag(s)@ @[<hov>%a@]@]"
1392-
print_tags fields
1400+
"@,\
1401+
@,\
1402+
The first polymorphic variant is @{<info>closed@} and doesn't include \
1403+
the %s: @{<error>%a@}.@,\
1404+
@,\
1405+
Possible solutions:\n\
1406+
\ - Either make the first variant @{<info>open@} so it can accept \
1407+
additional constructors. To do this, make sure the type starts with \
1408+
@{<info>[>@} instead of @{<info>[@}\n\
1409+
\ - Or add the missing %s to it."
1410+
constructors_txt print_tags fields constructors_txt
13931411
| (_ :: _ as fields), _, [], true ->
1412+
let constructors_txt =
1413+
if List.length fields = 1 then "constructor" else "constructors"
1414+
in
13941415
fprintf ppf
1395-
"@,@[The second variant type does not allow tag(s)@ @[<hov>%a@]@]"
1396-
print_tags fields
1416+
"@,\
1417+
@,\
1418+
The second polymorphic variant is @{<info>closed@} and doesn't \
1419+
include the %s: @{<error>%a@}.@,\
1420+
@,\
1421+
Possible solutions:\n\
1422+
\ - Either make the second variant @{<info>open@} so it can accept \
1423+
additional constructors. To do this, make sure the type starts with \
1424+
@{<info>[>@} instead of @{<info>[@}\n\
1425+
\ - Or add the missing %s to it."
1426+
constructors_txt print_tags fields constructors_txt
13971427
| [(l1, _)], true, [(l2, _)], true when l1 = l2 ->
1398-
fprintf ppf "@,Types for tag %s are incompatible"
1428+
fprintf ppf
1429+
"@,\
1430+
@,\
1431+
Both polymorphic variants have the constructor @{<info>%s@}, but \
1432+
their payload types are incompatible.@,\
1433+
Make sure the payload types for @{<info>%s@} match exactly in both \
1434+
polymorphic variants."
1435+
(!print_res_poly_identifier l1)
13991436
(!print_res_poly_identifier l1)
14001437
| _ -> ())
14011438
| _ -> ()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/polyvariant_constructor_payload_mismatch.res:4:16
4+
5+
2 │ type test2 = [#Click(string)]
6+
3 │ let a: test = #Click(1)
7+
4 │ let b: test2 = a
8+
5 │
9+
10+
This has type: test
11+
But it's expected to have type: test2
12+
13+
Both polymorphic variants have the constructor #Click, but their payload types are incompatible.
14+
Make sure the payload types for #Click match exactly in both polymorphic variants.
15+
16+
You can convert int to string with Int.toString.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/polyvariant_constructors_mismatch_second.res:7:16-22
4+
5+
5 │ }
6+
6 │
7+
7 │ let _ = handle(#Resize)
8+
8 │
9+
10+
This has type: [> #Resize]
11+
But this function argument is expecting: [#Click | #KeyDown]
12+
13+
The second polymorphic variant is closed and doesn't include the constructor: #Resize.
14+
15+
Possible solutions:
16+
- Either make the second variant open so it can accept additional constructors. To do this, make sure the type starts with [> instead of [
17+
- Or add the missing constructor to it.

tests/build_tests/super_errors/expected/polyvariant_name_formatting.res.expected

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@
1010

1111
This pattern matches values of type [? #Invalid]
1212
but a pattern was expected which matches values of type polyvariant
13-
The second variant type does not allow tag(s) #Invalid
13+
14+
The second polymorphic variant is closed and doesn't include the constructor: #Invalid.
15+
16+
Possible solutions:
17+
- Either make the second variant open so it can accept additional constructors. To do this, make sure the type starts with [> instead of [
18+
- Or add the missing constructor to it.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/polyvariants_no_overlap.res:2:19
4+
5+
1 │ let a: [#Resize | #KeyDown] = #Resize
6+
2 │ let b: [#Click] = a
7+
3 │
8+
9+
This has type: [#KeyDown | #Resize]
10+
But it's expected to have type: [#Click]
11+
12+
These polymorphic variants are incompatible - they share no common constructors.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
type test = [#Click(int)]
2+
type test2 = [#Click(string)]
3+
let a: test = #Click(1)
4+
let b: test2 = a
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let handle = (ev: [#Click | #KeyDown]) =>
2+
switch ev {
3+
| #Click => Js.log("clicked")
4+
| #KeyDown => Js.log("key down")
5+
}
6+
7+
let _ = handle(#Resize)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let a: [#Resize | #KeyDown] = #Resize
2+
let b: [#Click] = a

0 commit comments

Comments
 (0)