[m-rev.] for review: Fix bug with typechecking of coercions.

Peter Wang novalazy at gmail.com
Mon Jul 1 16:52:44 AEST 2024


Fix a problem where the order that the coercions are typechecked
can cause type variables to be bound differently.

For example, assuming citrus =< fruit:

    X = [] : list(T),
    coerce(X) = _ : list(fruit),
    coerce(X) = _ : list(citrus)

T could end up bound to either 'fruit' or 'citrus' depending on which
coercion is checked first, due to unification of the type variable in
the coerce argument type, and the target type of the coercion.
This is clearly wrong. Moreover, there is no reason that 'fruit' and
'citrus' are the only possibilities for T; there could be another
subtype for 'fruit' as well.

The fix here is to delay checking of any coercion until its argument
type is resolved, and to reject any coercion whose argument type
remains unresolved.

compiler/type_assign.m:
    Add status for coercion that could not be satisfied due to an
    unresolved type. Rename an existing constructor.

    Fix a comment.

compiler/typecheck_clauses.m:
    When checking coerce constraints after typechecking a clause,
    keep delaying any coercions with a non-ground "from" type.
    This prevents the problem, which is that a type variable in the
    "from" type of a coercion is unified with a type from the "to" type,
    affecting coercions processed later which also would bind the same
    type variable.

    If we cannot make progress on a set of delayed coercions, then mark
    all those coercions as unsatisfiable due to the argument type being
    unresolved.

compiler/typecheck.m:
    Generate errors for coercions with unresolved types.

compiler/typecheck_errors.m:
    Add predicate for reporting coercions with unresolved types.

    Add "error:" prefix to invalid coercion errors.

tests/invalid/coerce_ambig.m
    Delete out-of-date comments.

tests/invalid/coerce_ambig.err_exp
tests/invalid/coerce_infer.err_exp
tests/invalid/coerce_non_du.err_exp
tests/invalid/coerce_type_error.err_exp
tests/invalid/coerce_unify_tvars.err_exp
tests/invalid/coerce_void.err_exp
    Update expected outputs.

diff --git a/compiler/type_assign.m b/compiler/type_assign.m
index 8faac5965..6788de0c6 100644
--- a/compiler/type_assign.m
+++ b/compiler/type_assign.m
@@ -58,7 +58,7 @@
 
 :- type coerce_constraint
     --->    coerce_constraint(
-                % One or both sides should be a type_variable.
+                % One or both sides should contain a type variable.
                 coerce_from     :: mer_type,
                 coerce_to       :: mer_type,
                 coerce_context  :: prog_context,
@@ -67,7 +67,8 @@
 
 :- type coerce_constraint_status
     --->    need_to_check
-    ;       unsatisfiable
+    ;       unsatisfiable_invalid
+    ;       unsatisfiable_unresolved
     ;       satisfied_but_redundant.
 
 :- pred type_assign_get_var_types(type_assign::in,
diff --git a/compiler/typecheck.m b/compiler/typecheck.m
index 74ed33ad8..73750e2b4 100644
--- a/compiler/typecheck.m
+++ b/compiler/typecheck.m
@@ -1065,9 +1065,13 @@ report_coercion(TypeAssign, Coercion, !Info) :-
         Status = need_to_check,
         unexpected($pred, "need to check")
     ;
-        Status = unsatisfiable,
+        Status = unsatisfiable_invalid,
         Spec = report_invalid_coerce_from_to(ClauseContext, Context, TVarSet,
             FromType, ToType)
+    ;
+        Status = unsatisfiable_unresolved,
+        Spec = report_unresolved_coerce_from_to(ClauseContext, Context,
+            TVarSet, FromType, ToType)
     ;
         Status = satisfied_but_redundant,
         Spec = report_redundant_coerce(ClauseContext, Context, TVarSet,
diff --git a/compiler/typecheck_clauses.m b/compiler/typecheck_clauses.m
index f9cb66972..c7a7d1b8c 100644
--- a/compiler/typecheck_clauses.m
+++ b/compiler/typecheck_clauses.m
@@ -95,7 +95,6 @@
 :- import_module parse_tree.vartypes.
 
 :- import_module assoc_list.
-:- import_module bool.
 :- import_module int.
 :- import_module io.
 :- import_module map.
@@ -1799,7 +1798,7 @@ typecheck_coerce_2(Context, FromVar, ToVar, TypeAssign0,
             )
         else
             Coercion = coerce_constraint(FromType, ToType, Context,
-                unsatisfiable),
+                unsatisfiable_invalid),
             add_coerce_constraint(Coercion, TypeAssign0, TypeAssign)
         ),
         !:TypeAssignSet = [TypeAssign | !.TypeAssignSet]
@@ -2503,8 +2502,8 @@ compare_types_corresponding(TypeTable, TVarSet, Comparison,
 
 %---------------------------------------------------------------------------%
 
-    % Remove satisfied coerce constraints from each type assignment,
-    % then drop any type assignments with unsatisfied coerce constraints
+    % Check coerce constraints in each type assignment to see if they can be
+    % satisfied. Drop any type assignments with unsatisfied coerce constraints
     % if there is at least one type assignment that does satisfy coerce
     % constraints.
     %
@@ -2534,65 +2533,112 @@ type_assign_prune_coerce_constraints(TypeTable, !TypeAssign) :-
         Coercions0 = []
     ;
         Coercions0 = [_ | _],
-        check_pending_coerce_constraints(TypeTable, Coercions0, Coercions,
-            !TypeAssign),
+        check_pending_coerce_constraints_to_fixpoint(TypeTable,
+            Coercions0, Coercions, !TypeAssign),
         type_assign_set_coerce_constraints(Coercions, !TypeAssign)
     ).
 
-:- pred check_pending_coerce_constraints(type_table::in,
+:- pred check_pending_coerce_constraints_to_fixpoint(type_table::in,
     list(coerce_constraint)::in, list(coerce_constraint)::out,
     type_assign::in, type_assign::out) is det.
 
-check_pending_coerce_constraints(_TypeTable, [], [], !TypeAssign).
-check_pending_coerce_constraints(TypeTable, [Coercion0 | Coercions0],
-        KeepCoercions, !TypeAssign) :-
+check_pending_coerce_constraints_to_fixpoint(TypeTable, Coercions0, Coercions,
+        !TypeAssign) :-
+    check_pending_coerce_constraints_loop(TypeTable, Coercions0,
+        KeepCoercions, DelayedCoercions, !TypeAssign),
+    ( if
+        KeepCoercions = [],
+        list.length(Coercions0) = list.length(DelayedCoercions) : int
+    then
+        % All coerce constraints were delayed; give up.
+        list.map(set_coerce_constraint_to_unresolved,
+            DelayedCoercions, Coercions)
+    else
+        check_pending_coerce_constraints_to_fixpoint(TypeTable,
+            DelayedCoercions, Coercions2, !TypeAssign),
+        Coercions = KeepCoercions ++ Coercions2
+    ).
+
+:- pred check_pending_coerce_constraints_loop(type_table::in,
+    list(coerce_constraint)::in, list(coerce_constraint)::out,
+    list(coerce_constraint)::out, type_assign::in, type_assign::out) is det.
+
+check_pending_coerce_constraints_loop(_TypeTable, [], [], [], !TypeAssign).
+check_pending_coerce_constraints_loop(TypeTable, [Coercion0 | Coercions0],
+        KeepCoercions, DelayedCoercions, !TypeAssign) :-
+    check_coerce_constraint_if_ready(TypeTable, Coercion0, CheckResult,
+        !TypeAssign),
+    (
+        CheckResult = prune,
+        check_pending_coerce_constraints_loop(TypeTable, Coercions0,
+            KeepCoercions, DelayedCoercions, !TypeAssign)
+    ;
+        CheckResult = keep(Coercion),
+        check_pending_coerce_constraints_loop(TypeTable, Coercions0,
+            TailKeepCoercions, DelayedCoercions, !TypeAssign),
+        KeepCoercions = [Coercion | TailKeepCoercions]
+    ;
+        CheckResult = delay,
+        check_pending_coerce_constraints_loop(TypeTable, Coercions0,
+            KeepCoercions, TailDelayedCoercions, !TypeAssign),
+        DelayedCoercions = [Coercion0 | TailDelayedCoercions]
+    ).
+
+:- type check_coerce_constraint_action
+    --->    prune
+    ;       keep(coerce_constraint)
+    ;       delay.
+
+:- pred check_coerce_constraint_if_ready(type_table::in, coerce_constraint::in,
+    check_coerce_constraint_action::out, type_assign::in, type_assign::out)
+    is det.
+
+check_coerce_constraint_if_ready(TypeTable, Coercion0, Action, !TypeAssign) :-
     Coercion0 = coerce_constraint(FromType0, ToType0, Context, Status0),
     (
         Status0 = need_to_check,
         TypeAssign0 = !.TypeAssign,
-        type_assign_get_type_bindings(TypeAssign0, TypeBindings0),
         type_assign_get_typevarset(TypeAssign0, TVarSet),
+        type_assign_get_existq_tvars(TypeAssign0, ExistQTVars),
+        type_assign_get_type_bindings(TypeAssign0, TypeBindings0),
         apply_rec_subst_to_type(TypeBindings0, FromType0, FromType),
         apply_rec_subst_to_type(TypeBindings0, ToType0, ToType),
-        ( if
-            typecheck_coerce_between_types(TypeTable, TVarSet,
-                FromType, ToType, TypeAssign0, TypeAssign1)
-        then
-            type_assign_get_type_bindings(TypeAssign1, TypeBindings1),
-            ( if is_same_type_after_subst(TypeBindings1, FromType, ToType) then
-                Keep = yes,
-                Coercion = coerce_constraint(FromType, ToType, Context,
-                    satisfied_but_redundant)
+        ( if type_is_ground_except_vars(FromType, ExistQTVars) then
+            ( if
+                typecheck_coerce_between_types(TypeTable, TVarSet,
+                    FromType, ToType, TypeAssign0, TypeAssign1)
+            then
+                type_assign_get_type_bindings(TypeAssign1, TypeBindings1),
+                ( if is_same_type_after_subst(TypeBindings1, FromType, ToType) then
+                    Coercion = coerce_constraint(FromType, ToType, Context,
+                        satisfied_but_redundant),
+                    Action = keep(Coercion)
+                else
+                    Action = prune
+                ),
+                !:TypeAssign = TypeAssign1
             else
-                Keep = no,
-                Coercion = Coercion0
-            ),
-            % XXX This biases the typechecker to type bindings made by coerce
-            % constraints that happen to be processed earlier.
-            !:TypeAssign = TypeAssign1
+                Coercion = coerce_constraint(FromType0, ToType0, Context,
+                    unsatisfiable_invalid),
+                Action = keep(Coercion)
+            )
         else
-            Keep = yes,
-            Coercion = coerce_constraint(FromType0, ToType0, Context,
-                unsatisfiable)
+            Action = delay
         )
     ;
-        ( Status0 = unsatisfiable
+        ( Status0 = unsatisfiable_invalid
+        ; Status0 = unsatisfiable_unresolved
         ; Status0 = satisfied_but_redundant
         ),
-        Keep = yes,
-        Coercion = Coercion0
-    ),
-    (
-        Keep = yes,
-        check_pending_coerce_constraints(TypeTable, Coercions0,
-            TailKeepCoercions, !TypeAssign),
-        KeepCoercions = [Coercion | TailKeepCoercions]
-    ;
-        Keep = no,
-        check_pending_coerce_constraints(TypeTable, Coercions0,
-            KeepCoercions, !TypeAssign)
+        Action = keep(Coercion0)
     ).
 
+:- pred set_coerce_constraint_to_unresolved(coerce_constraint::in,
+    coerce_constraint::out) is det.
+
+set_coerce_constraint_to_unresolved(!Coercion) :-
+    !Coercion ^ coerce_status := unsatisfiable_unresolved.
+
 :- pred type_assign_has_only_satisfied_coerce_constraints(type_assign::in)
     is semidet.
 
@@ -2605,12 +2651,10 @@ type_assign_has_only_satisfied_coerce_constraints(TypeAssign) :-
 coerce_constraint_is_satisfied(Coercion) :-
     Coercion = coerce_constraint(_FromType, _ToType, _Context, Status),
     require_complete_switch [Status]
-    (
-        Status = need_to_check, fail
-    ;
-        Status = unsatisfiable, fail
-    ;
-        Status = satisfied_but_redundant
+    ( Status = need_to_check, fail
+    ; Status = unsatisfiable_invalid, fail
+    ; Status = unsatisfiable_unresolved, fail
+    ; Status = satisfied_but_redundant
     ).
 
 %---------------------------------------------------------------------------%
diff --git a/compiler/typecheck_errors.m b/compiler/typecheck_errors.m
index 035e3671a..c8234e550 100644
--- a/compiler/typecheck_errors.m
+++ b/compiler/typecheck_errors.m
@@ -44,6 +44,9 @@
 :- func report_invalid_coerce_from_to(type_error_clause_context, prog_context,
     tvarset, mer_type, mer_type) = error_spec.
 
+:- func report_unresolved_coerce_from_to(type_error_clause_context,
+    prog_context, tvarset, mer_type, mer_type) = error_spec.
+
 :- func report_redundant_coerce(type_error_clause_context, prog_context,
     tvarset, mer_type) = error_spec.
 
@@ -280,10 +283,11 @@ report_invalid_coerce_from_to(ClauseContext, Context, TVarSet,
     % XXX TYPECHECK_ERRORS
     % This code can generate some less-than-helpful diagnostics.
     %
-    % - For tests/invalid/coerce_infer.m and some others, it says that
+    % - For tests/invalid/coerce_unify_tvars.m and some others, it says that
     %   you cannot coerce from one anonymous type variable to another.
     %
     % Is there something we can report that would be more helpful?
+    % In most cases, we report that the coerced argument type is unresolved.
     InClauseForPieces = in_clause_for_pieces(ClauseContext),
     FromTypeStr = mercury_type_to_string(TVarSet, print_num_only, FromType),
     ToTypeStr = mercury_type_to_string(TVarSet, print_num_only, ToType),
@@ -347,13 +351,25 @@ report_invalid_coerce_from_to(ClauseContext, Context, TVarSet,
             )
         )
     ),
-    ErrorPieces = [words("cannot coerce from")] ++
+    ErrorPieces = [words("error: cannot coerce from")] ++
         color_as_inconsistent([quote(FromTypeStr)]) ++ [words("to")] ++
         color_as_inconsistent([quote(ToTypeStr), suffix(".")]) ++ [nl] ++
         CausePieces ++ [nl],
     Spec = spec($pred, severity_error, phase_type_check, Context,
         InClauseForPieces ++ ErrorPieces).
 
+report_unresolved_coerce_from_to(ClauseContext, Context, TVarSet,
+        FromType, ToType) = Spec :-
+    InClauseForPieces = in_clause_for_pieces(ClauseContext),
+    FromTypeStr = mercury_type_to_string(TVarSet, print_num_only, FromType),
+    ToTypeStr = mercury_type_to_string(TVarSet, print_num_only, ToType),
+    ErrorPieces = [words("error: cannot coerce from")] ++
+        [quote(FromTypeStr), words("to"), quote(ToTypeStr), suffix("."), nl] ++
+        [words("The type of the coerced argument is")] ++
+        color_as_incorrect([words("unresolved.")]) ++ [nl],
+    Spec = spec($pred, severity_error, phase_type_check, Context,
+        InClauseForPieces ++ ErrorPieces).
+
 report_redundant_coerce(ClauseContext, Context, TVarSet, FromType) = Spec :-
     InClauseForPieces = in_clause_for_pieces(ClauseContext),
     FromTypeStr = mercury_type_to_string(TVarSet, print_num_only, FromType),
diff --git a/tests/invalid/coerce_ambig.err_exp b/tests/invalid/coerce_ambig.err_exp
index 43b3df14e..55ac78b04 100644
--- a/tests/invalid/coerce_ambig.err_exp
+++ b/tests/invalid/coerce_ambig.err_exp
@@ -10,12 +10,16 @@ coerce_ambig.m:036:   The following variables have ambiguous types:
 coerce_ambig.m:036:   The variable `X' can have either of the following types:
 coerce_ambig.m:036:     coerce_ambig.another_fruit,
 coerce_ambig.m:036:     coerce_ambig.fruit.
-coerce_ambig.m:044: In clause for predicate `ambig3'/1:
-coerce_ambig.m:044:   warning: type conversion from
-coerce_ambig.m:044:   `coerce_ambig.list(coerce_ambig.fruit)' to the same type
-coerce_ambig.m:044:   is redundant.
-coerce_ambig.m:053: In clause for predicate `ambig4'/0:
-coerce_ambig.m:053:   warning: type conversion from
-coerce_ambig.m:053:   `coerce_ambig.list(coerce_ambig.fruit)' to the same type
-coerce_ambig.m:053:   is redundant.
+coerce_ambig.m:043: In clause for predicate `ambig3'/1:
+coerce_ambig.m:043:   error: cannot coerce from `coerce_ambig.list(V_1)' to
+coerce_ambig.m:043:   `coerce_ambig.list(coerce_ambig.fruit)'.
+coerce_ambig.m:043:   The type of the coerced argument is unresolved.
+coerce_ambig.m:049: In clause for predicate `ambig4'/0:
+coerce_ambig.m:049:   error: cannot coerce from `coerce_ambig.list(V_1)' to
+coerce_ambig.m:049:   `coerce_ambig.list(coerce_ambig.citrus)'.
+coerce_ambig.m:049:   The type of the coerced argument is unresolved.
+coerce_ambig.m:050: In clause for predicate `ambig4'/0:
+coerce_ambig.m:050:   error: cannot coerce from `coerce_ambig.list(V_1)' to
+coerce_ambig.m:050:   `coerce_ambig.list(coerce_ambig.fruit)'.
+coerce_ambig.m:050:   The type of the coerced argument is unresolved.
 For more information, recompile with `-E'.
diff --git a/tests/invalid/coerce_ambig.m b/tests/invalid/coerce_ambig.m
index 342f658d5..636b6b9f7 100644
--- a/tests/invalid/coerce_ambig.m
+++ b/tests/invalid/coerce_ambig.m
@@ -40,14 +40,11 @@ ambig2 :-
 :- pred ambig3(list(fruit)::out) is det.
 
 ambig3(Xs) :-
-    % Not ambiguous after we unify list(_T) with list(fruit).
     Xs = coerce([]).
 
 :- pred ambig4 is det.
 
 ambig4 :-
-    % XXX The compiler wrongly picks X : list(fruit) or X : list(citrus)
-    % when it has no reason to reject the other possibility.
     X = [],
     coerce(X) = _ : list(citrus),
     coerce(X) = _ : list(fruit).
diff --git a/tests/invalid/coerce_infer.err_exp b/tests/invalid/coerce_infer.err_exp
index 92ee16f6b..e3e6331cb 100644
--- a/tests/invalid/coerce_infer.err_exp
+++ b/tests/invalid/coerce_infer.err_exp
@@ -1,4 +1,3 @@
 coerce_infer.m:026: In clause for function `f'/1:
-coerce_infer.m:026:   cannot coerce from `V_1' to `V_2'.
-coerce_infer.m:026:   You can only coerce from one discriminated union type to
-coerce_infer.m:026:   another, and `V_1' and `V_2' are type variables.
+coerce_infer.m:026:   error: cannot coerce from `V_1' to `V_2'.
+coerce_infer.m:026:   The type of the coerced argument is unresolved.
diff --git a/tests/invalid/coerce_non_du.err_exp b/tests/invalid/coerce_non_du.err_exp
index 121755890..8830a182c 100644
--- a/tests/invalid/coerce_non_du.err_exp
+++ b/tests/invalid/coerce_non_du.err_exp
@@ -1,21 +1,22 @@
 coerce_non_du.m:015: In clause for function `f1'/1:
-coerce_non_du.m:015:   cannot coerce from `int' to `int'.
+coerce_non_du.m:015:   error: cannot coerce from `int' to `int'.
 coerce_non_du.m:015:   You can only coerce from one discriminated union type to
 coerce_non_du.m:015:   another, and `int' is a builtin type.
 coerce_non_du.m:019: In clause for function `f2'/1:
-coerce_non_du.m:019:   cannot coerce from `float' to `float'.
+coerce_non_du.m:019:   error: cannot coerce from `float' to `float'.
 coerce_non_du.m:019:   You can only coerce from one discriminated union type to
 coerce_non_du.m:019:   another, and `float' is a builtin type.
 coerce_non_du.m:023: In clause for function `f3'/1:
-coerce_non_du.m:023:   cannot coerce from `{}' to `{}'.
+coerce_non_du.m:023:   error: cannot coerce from `{}' to `{}'.
 coerce_non_du.m:023:   You can only coerce from one discriminated union type to
 coerce_non_du.m:023:   another, and `{}' is a tuple type.
 coerce_non_du.m:027: In clause for function `f4'/1:
-coerce_non_du.m:027:   cannot coerce from `((func int) = int)' to
+coerce_non_du.m:027:   error: cannot coerce from `((func int) = int)' to
 coerce_non_du.m:027:   `((func int) = int)'.
 coerce_non_du.m:027:   You can only coerce from one discriminated union type to
 coerce_non_du.m:027:   another, and `((func int) = int)' is a function type.
 coerce_non_du.m:031: In clause for function `f5'/1:
-coerce_non_du.m:031:   cannot coerce from `pred(int, int)' to `pred(int, int)'.
+coerce_non_du.m:031:   error: cannot coerce from `pred(int, int)' to
+coerce_non_du.m:031:   `pred(int, int)'.
 coerce_non_du.m:031:   You can only coerce from one discriminated union type to
 coerce_non_du.m:031:   another, and `pred(int, int)' is a predicate type.
diff --git a/tests/invalid/coerce_type_error.err_exp b/tests/invalid/coerce_type_error.err_exp
index d873b9772..ff670e9ae 100644
--- a/tests/invalid/coerce_type_error.err_exp
+++ b/tests/invalid/coerce_type_error.err_exp
@@ -1,22 +1,23 @@
 coerce_type_error.m:045: In clause for predicate `bad_unrelated'/2:
-coerce_type_error.m:045:   cannot coerce from
+coerce_type_error.m:045:   error: cannot coerce from
 coerce_type_error.m:045:   `coerce_type_error.orange_non_fruit' to
 coerce_type_error.m:045:   `coerce_type_error.citrus'.
 coerce_type_error.m:070: In clause for predicate `bad_phantom'/2:
-coerce_type_error.m:070:   cannot coerce from `coerce_type_error.phantom(int)'
-coerce_type_error.m:070:   to `coerce_type_error.phantom(float)'.
+coerce_type_error.m:070:   error: cannot coerce from
+coerce_type_error.m:070:   `coerce_type_error.phantom(int)' to
+coerce_type_error.m:070:   `coerce_type_error.phantom(float)'.
 coerce_type_error.m:091: In clause for predicate `bad_higher_order'/2:
-coerce_type_error.m:091:   cannot coerce from
+coerce_type_error.m:091:   error: cannot coerce from
 coerce_type_error.m:091:   `coerce_type_error.wrap_ho(coerce_type_error.citrus)'
 coerce_type_error.m:091:   to
 coerce_type_error.m:091:   `coerce_type_error.wrap_ho(coerce_type_error.fruit)'.
 coerce_type_error.m:106: In clause for predicate `bad_foreign_type'/2:
-coerce_type_error.m:106:   cannot coerce from
+coerce_type_error.m:106:   error: cannot coerce from
 coerce_type_error.m:106:   `coerce_type_error.wrap_ft(coerce_type_error.citrus)'
 coerce_type_error.m:106:   to
 coerce_type_error.m:106:   `coerce_type_error.wrap_ft(coerce_type_error.fruit)'.
 coerce_type_error.m:118: In clause for predicate `bad_abs_type'/2:
-coerce_type_error.m:118:   cannot coerce from
+coerce_type_error.m:118:   error: cannot coerce from
 coerce_type_error.m:118:   `coerce_type_error.wrap_abs(coerce_type_error.citrus)'
 coerce_type_error.m:118:   to
 coerce_type_error.m:118:   `coerce_type_error.wrap_abs(coerce_type_error.fruit)'.
diff --git a/tests/invalid/coerce_unify_tvars.err_exp b/tests/invalid/coerce_unify_tvars.err_exp
index 8ed2f6d26..8d5379f57 100644
--- a/tests/invalid/coerce_unify_tvars.err_exp
+++ b/tests/invalid/coerce_unify_tvars.err_exp
@@ -1,3 +1,3 @@
 coerce_unify_tvars.m:063: In clause for predicate `head_type_params'/2:
-coerce_unify_tvars.m:063:   cannot coerce from `list.list(V_1)' to
+coerce_unify_tvars.m:063:   error: cannot coerce from `list.list(V_1)' to
 coerce_unify_tvars.m:063:   `list.list(V_2)'.
diff --git a/tests/invalid/coerce_void.err_exp b/tests/invalid/coerce_void.err_exp
index 021789c65..d096ed158 100644
--- a/tests/invalid/coerce_void.err_exp
+++ b/tests/invalid/coerce_void.err_exp
@@ -1,10 +1,4 @@
-coerce_void.m:010: Warning: unresolved polymorphism in predicate `main'/2.
-coerce_void.m:010:   The variables with unbound types were:
-coerce_void.m:010:     `List': list.list(T),
-coerce_void.m:010:     `V_8':  list.list(T).
-coerce_void.m:010:   The unbound type variable `T' will be implicitly bound to
-coerce_void.m:010:   the builtin type `void'.
 coerce_void.m:019: In clause for predicate `main'/2:
-coerce_void.m:019:   warning: type conversion from `list.list(V_1)' to the same
-coerce_void.m:019:   type is redundant.
-For more information, recompile with `-E'.
+coerce_void.m:019:   error: cannot coerce from `list.list(V_2)' to
+coerce_void.m:019:   `list.list(V_1)'.
+coerce_void.m:019:   The type of the coerced argument is unresolved.
-- 
2.44.0



More information about the reviews mailing list