[m-rev.] for review: Improve coerce mode error messages.
Peter Wang
novalazy at gmail.com
Wed May 12 14:09:08 AEST 2021
compiler/mode_errors.m:
Replace the two existing options for coerce mode errors with one
that includes:
- the path to the input subterm where the error was detected
- the type of the input subterm
- the target type that the subterm should be converted to
- the reason for the error
compiler/modecheck_coerce.m:
Modify the code such to produce the information above.
Semidet predicates that previously failed when an error is detected
are changed to det predicates that return the error information.
In a couple of places where predicates previously would fail,
we now abort because the conditions should not occur.
tests/invalid/Mmakefile:
tests/invalid/coerce_mode_error2.err_exp:
tests/invalid/coerce_mode_error2.m:
Add test case.
tests/invalid/coerce_clobbered.err_exp:
tests/invalid/coerce_instvar.err_exp:
tests/invalid/coerce_int.err_exp:
tests/invalid/coerce_mode_error.err_exp:
tests/invalid/coerce_recursive_inst.err_exp:
tests/invalid/coerce_recursive_type.err_exp:
tests/invalid/coerce_unreachable.err_exp:
Update expected error messages.
diff --git a/compiler/mode_errors.m b/compiler/mode_errors.m
index 0dd398710..c30df7eb3 100644
--- a/compiler/mode_errors.m
+++ b/compiler/mode_errors.m
@@ -162,12 +162,8 @@
% Mode errors in coerce expressions.
- ; mode_error_coerce_input_not_ground(prog_var, mer_inst)
- % The argument in a coerce expression has a non-ground inst.
-
- ; mode_error_coerce_ground_invalid(mer_inst, mer_type)
- % The argument in a coerce expression has a ground inst
- % that is not valid for the result type.
+ ; mode_error_coerce_error(coerce_error)
+ % Mode error in coerce expression.
% Mode errors that can happen in more than one kind of goal.
@@ -271,6 +267,28 @@
%---------------------%
+:- type coerce_error
+ ---> coerce_error(
+ % Path to subterm where the mode error was detected.
+ list(coerce_error_term_path_step),
+ % Type of the subterm.
+ mer_type,
+ % Target type of the conversion.
+ mer_type,
+ coerce_error_reason
+ ).
+
+:- type coerce_error_term_path_step
+ ---> coerce_error_term_path_step(cons_id, int).
+
+:- type coerce_error_reason
+ ---> input_inst_not_ground(mer_inst)
+ ; invalid_inst_for_input_type(mer_inst)
+ ; invalid_cons_ids_for_result_type(list(cons_id))
+ ; has_inst_expect_upcast(mer_inst).
+
+%---------------------%
+
:- type final_inst_error
---> too_instantiated
; not_instantiated_enough
@@ -471,12 +489,8 @@ mode_error_to_spec(ModeInfo, ModeError) = Spec :-
Spec = mode_error_merge_disj_to_spec(ModeInfo, MergeContext,
MergeErrors)
;
- ModeError = mode_error_coerce_input_not_ground(Var, VarInst),
- Spec = mode_error_coerce_input_not_ground_to_spec(ModeInfo, Var,
- VarInst)
- ;
- ModeError = mode_error_coerce_ground_invalid(Inst, Type),
- Spec = mode_error_coerce_ground_invalid_to_spec(ModeInfo, Inst, Type)
+ ModeError = mode_error_coerce_error(CoerceError),
+ Spec = mode_error_coerce_error_to_spec(ModeInfo, CoerceError)
;
ModeError = mode_error_bind_locked_var(Reason, Var, InstA, InstB),
Spec = mode_error_bind_locked_var_to_spec(ModeInfo, Reason, Var,
@@ -1308,38 +1322,102 @@ merge_context_to_string(merge_stm_atomic) = "atomic".
%---------------------------------------------------------------------------%
-:- func mode_error_coerce_input_not_ground_to_spec(mode_info, prog_var,
- mer_inst) = error_spec.
+:- func mode_error_coerce_error_to_spec(mode_info, coerce_error) = error_spec.
-mode_error_coerce_input_not_ground_to_spec(ModeInfo, Var, VarInst) = Spec :-
+mode_error_coerce_error_to_spec(ModeInfo, Error) = Spec :-
+ Error = coerce_error(TermPath, FromType, ToType, Reason),
Preamble = mode_info_context_preamble(ModeInfo),
mode_info_get_context(ModeInfo, Context),
- mode_info_get_varset(ModeInfo, VarSet),
- Pieces = [words("mode error:"),
- quote(mercury_var_to_name_only(VarSet, Var))] ++
- has_instantiatedness(ModeInfo, VarInst, ",") ++
- [words("but it must be ground."), nl],
+ varset.init(TypeVarSet),
+ (
+ TermPath = [],
+ TermPathPieces = [],
+ TheTerm = words("the input term")
+ ;
+ TermPath = [_ | _],
+ TermPathPieces =
+ [words("in the input term:"), nl] ++
+ list.condense(list.map(make_term_path_piece, TermPath)),
+ TheTerm = words("the subterm")
+ ),
+ FromTypeStr =
+ mercury_type_to_string(TypeVarSet, print_name_only, FromType),
+ ToTypeStr =
+ mercury_type_to_string(TypeVarSet, print_name_only, ToType),
+ (
+ Reason = input_inst_not_ground(Inst),
+ ReasonPieces =
+ [TheTerm, words("has instantiatedness")] ++
+ report_inst(ModeInfo, quote_short_inst, [suffix(",")],
+ [nl_indent_delta(1)], [suffix(","), nl_indent_delta(-1)],
+ Inst) ++
+ [words("but it must be ground."), nl]
+ ;
+ Reason = invalid_inst_for_input_type(Inst),
+ ReasonPieces =
+ [TheTerm, words("has instantiatedness")] ++
+ report_inst(ModeInfo, quote_short_inst, [suffix(",")],
+ [nl_indent_delta(1)], [suffix(","), nl_indent_delta(-1)],
+ Inst) ++
+ [words("which is invalid for the type"), quote(FromTypeStr),
+ suffix("."), nl]
+ ;
+ Reason = invalid_cons_ids_for_result_type(ConsIds),
+ ConsIdsStrs = list.map(unqualify_cons_id_to_string, ConsIds),
+ ReasonPieces =
+ [words("cannot convert"), TheTerm,
+ words("from type"), quote(FromTypeStr),
+ words("to"), quote(ToTypeStr),
+ words("because its instantiatedness includes the function"),
+ words(choose_number(ConsIds, "symbol", "symbols"))] ++
+ list_to_quoted_pieces(ConsIdsStrs) ++
+ [suffix("."), nl]
+ ;
+ Reason = has_inst_expect_upcast(Inst),
+ ReasonPieces =
+ [words("cannot convert"), TheTerm,
+ words("from type"), quote(FromTypeStr),
+ words("to"), quote(ToTypeStr),
+ words("because it has instantiatedness")] ++
+ report_inst(ModeInfo, quote_short_inst, [suffix(",")],
+ [nl_indent_delta(1)], [suffix(","), nl_indent_delta(-1)],
+ Inst) ++
+ [words("and the former type is not a subtype of the latter type."),
+ nl]
+ ),
+ Pieces = [words("mode error:")] ++ TermPathPieces ++ ReasonPieces,
Spec = simplest_spec($pred, severity_error,
phase_mode_check(report_in_any_mode), Context, Preamble ++ Pieces).
-%---------------------------------------------------------------------------%
+:- func make_term_path_piece(coerce_error_term_path_step) =
+ list(format_component).
-:- func mode_error_coerce_ground_invalid_to_spec(mode_info, mer_inst, mer_type)
- = error_spec.
+make_term_path_piece(Step) = Pieces :-
+ Step = coerce_error_term_path_step(ConsId, ArgNum),
+ ( if
+ ConsId = cons(_SymName, 1, _),
+ ArgNum = 1
+ then
+ ArgPieces = [words("in the argument")]
+ else
+ ArgPieces = [words("in argument"), int_fixed(ArgNum)]
+ ),
+ ConsIdStr = unqualify_cons_id_to_string(ConsId),
+ Pieces = ArgPieces ++
+ [words("of function symbol"), quote(ConsIdStr), suffix(":"), nl].
-mode_error_coerce_ground_invalid_to_spec(ModeInfo, Inst, Type) = Spec :-
- Preamble = mode_info_context_preamble(ModeInfo),
- mode_info_get_context(ModeInfo, Context),
- varset.init(TypeVarSet),
- Pieces = [words("mode error: the input term has instantiatedness")] ++
- report_inst(ModeInfo, quote_short_inst, [suffix(","), nl],
- [nl_indent_delta(1)], [suffix(","), nl_indent_delta(-1)],
- Inst) ++
- [words("and cannot be converted to the type"),
- quote(mercury_type_to_string(TypeVarSet, print_name_only, Type)),
- suffix("."), nl],
- Spec = simplest_spec($pred, severity_error,
- phase_mode_check(report_in_any_mode), Context, Preamble ++ Pieces).
+:- func unqualify_cons_id_to_string(cons_id) = string.
+
+unqualify_cons_id_to_string(ConsId0) = Str :-
+ ( if ConsId0 = cons(Name0, Arity, TypeCtor) then
+ UnqualName = unqualify_name(Name0),
+ Name = unqualified(UnqualName),
+ ConsId = cons(Name, Arity, TypeCtor)
+ else
+ ConsId = ConsId0
+ ),
+ Str = mercury_cons_id_to_string(output_mercury, does_not_need_brackets,
+ ConsId).
%---------------------------------------------------------------------------%
diff --git a/compiler/modecheck_coerce.m b/compiler/modecheck_coerce.m
index f76b6beab..5f38f753d 100644
--- a/compiler/modecheck_coerce.m
+++ b/compiler/modecheck_coerce.m
@@ -53,6 +53,7 @@
:- import_module parse_tree.prog_type_subst.
:- import_module parse_tree.set_of_var.
+:- import_module int.
:- import_module map.
:- import_module maybe.
:- import_module pair.
@@ -61,6 +62,9 @@
:- import_module term.
:- import_module varset.
+:- inst cons for cons_id/0
+ ---> cons(ground, ground, ground).
+
:- type modecheck_coerce_result
---> coerce_mode_ok(
list(prog_var),
@@ -69,8 +73,11 @@
)
; coerce_mode_error.
-:- inst cons for cons_id/0
- ---> cons(ground, ground, ground).
+:- type rev_term_path == list(coerce_error_term_path_step).
+
+:- type maybe_coerce_error(T)
+ ---> ok(T)
+ ; coerce_error(coerce_error).
%---------------------------------------------------------------------------%
@@ -79,14 +86,17 @@ modecheck_coerce(Args0, Args, Modes0, Modes, Det, ExtraGoals, !ModeInfo) :-
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
mode_info_get_instmap(!.ModeInfo, InstMap),
( if instmap_is_reachable(InstMap) then
+ mode_info_get_var_types(!.ModeInfo, VarTypes),
+ lookup_var_type(VarTypes, X, TypeX),
+ lookup_var_type(VarTypes, Y, TypeY),
instmap_lookup_var(InstMap, X, InstX),
instmap_lookup_var(InstMap, Y, InstY),
( if
inst_is_ground(ModuleInfo0, InstX),
not inst_is_clobbered(ModuleInfo0, InstX)
then
- modecheck_coerce_vars(ModuleInfo0, X, Y, InstX, InstY, Res,
- !ModeInfo),
+ modecheck_coerce_vars(ModuleInfo0, X, Y, TypeX, TypeY,
+ InstX, InstY, Res, !ModeInfo),
(
Res = coerce_mode_ok(Args, Modes, ExtraGoals),
Det = detism_det
@@ -99,7 +109,9 @@ modecheck_coerce(Args0, Args, Modes0, Modes, Det, ExtraGoals, !ModeInfo) :-
)
else
WaitingVars = set_of_var.make_singleton(X),
- ModeError = mode_error_coerce_input_not_ground(X, InstX),
+ Reason = input_inst_not_ground(InstX),
+ Error = coerce_error([], TypeX, TypeY, Reason),
+ ModeError = mode_error_coerce_error(Error),
mode_info_error(WaitingVars, ModeError, !ModeInfo),
Args = [X, Y],
Modes = Modes0,
@@ -117,18 +129,15 @@ modecheck_coerce(Args0, Args, Modes0, Modes, Det, ExtraGoals, !ModeInfo) :-
).
:- pred modecheck_coerce_vars(module_info::in, prog_var::in, prog_var::in,
- mer_inst::in, mer_inst::in, modecheck_coerce_result::out,
- mode_info::in, mode_info::out) is det.
+ mer_type::in, mer_type::in, mer_inst::in, mer_inst::in,
+ modecheck_coerce_result::out, mode_info::in, mode_info::out) is det.
-modecheck_coerce_vars(ModuleInfo0, X, Y, InstX, InstY, Res, !ModeInfo) :-
+modecheck_coerce_vars(ModuleInfo0, X, Y, TypeX, TypeY, InstX, InstY, Res,
+ !ModeInfo) :-
mode_info_get_pred_id(!.ModeInfo, PredId),
module_info_pred_info(ModuleInfo0, PredId, PredInfo),
pred_info_get_typevarset(PredInfo, TVarSet),
- mode_info_get_var_types(!.ModeInfo, VarTypes),
- lookup_var_type(VarTypes, X, TypeX),
- lookup_var_type(VarTypes, Y, TypeY),
-
mode_info_var_is_live(!.ModeInfo, X, LiveX),
mode_info_var_is_live(!.ModeInfo, Y, LiveY),
( if LiveX = is_live, LiveY = is_live then
@@ -137,11 +146,12 @@ modecheck_coerce_vars(ModuleInfo0, X, Y, InstX, InstY, Res, !ModeInfo) :-
BothLive = is_dead
),
+ RevTermPath0 = [],
set.init(ExpandedInsts0),
- modecheck_coerce_make_inst(ModuleInfo0, TVarSet, LiveX, TypeX, TypeY,
- ExpandedInsts0, InstX, MaybeFinalInstY),
+ modecheck_coerce_make_inst(ModuleInfo0, TVarSet, LiveX, RevTermPath0,
+ TypeX, TypeY, ExpandedInsts0, InstX, MaybeFinalInstY),
(
- MaybeFinalInstY = yes(FinalInstY),
+ MaybeFinalInstY = ok(FinalInstY),
( if
abstractly_unify_inst(BothLive, InstX, ground_inst, real_unify,
UnifyInstX, _Det, ModuleInfo0, ModuleInfo1)
@@ -171,9 +181,9 @@ modecheck_coerce_vars(ModuleInfo0, X, Y, InstX, InstY, Res, !ModeInfo) :-
Res = coerce_mode_ok([X, YPrime], [ModeX, ModeYPrime], ExtraGoals)
)
;
- MaybeFinalInstY = no,
- ModeError = mode_error_coerce_ground_invalid(InstX, TypeY),
+ MaybeFinalInstY = coerce_error(Error),
set_of_var.init(WaitingVars),
+ ModeError = mode_error_coerce_error(Error),
mode_info_error(WaitingVars, ModeError, !ModeInfo),
Res = coerce_mode_error
).
@@ -195,16 +205,16 @@ create_fresh_var(VarType, Var, !ModeInfo) :-
% Try to produce the resulting inst of a coercion from TypeX to TypeY.
% InstX is the initial inst of the input term.
- % If the coercion is mode correct, then Res is bound to 'yes(InstY)' where
+ % If the coercion is mode correct, then Res is bound to 'ok(InstY)' where
% InstY is the final inst of the result term.
- % If there is a mode error, Res is bound to 'no'.
+ % If there is a mode error, Res is bound to 'coerce_error(Error)'.
%
:- pred modecheck_coerce_make_inst(module_info::in, tvarset::in, is_live::in,
- mer_type::in, mer_type::in, expanded_insts::in,
- mer_inst::in, maybe(mer_inst)::out) is det.
+ rev_term_path::in, mer_type::in, mer_type::in, expanded_insts::in,
+ mer_inst::in, maybe_coerce_error(mer_inst)::out) is det.
-modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, TypeX, TypeY,
- ExpandedInsts0, InstX, Res) :-
+modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, RevTermPath0,
+ TypeX, TypeY, ExpandedInsts0, InstX, Res) :-
(
( InstX = free
; InstX = free(_)
@@ -216,24 +226,25 @@ modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, TypeX, TypeY,
;
InstX = bound(UniqX, _InstResultsX, FunctorsX),
modecheck_coerce_from_bound_make_bound_inst(ModuleInfo, TVarSet, LiveX,
- UniqX, TypeX, TypeY, ExpandedInsts0, FunctorsX, Res)
+ UniqX, RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX,
+ FunctorsX, Res)
;
InstX = ground(UniqX, HOInstInfoX),
(
HOInstInfoX = none_or_default_func,
- modecheck_coerce_from_ground_make_inst(ModuleInfo, TVarSet,
- LiveX, UniqX, TypeX, TypeY, Res)
+ modecheck_coerce_from_ground_make_inst(ModuleInfo, TVarSet, LiveX,
+ UniqX, RevTermPath0, TypeX, TypeY, InstX, Res)
;
HOInstInfoX = higher_order(_PredInstInfoX),
UniqY = uniqueness_for_coerce_result(LiveX, UniqX),
% Coerce cannot change the calling convention.
InstY = ground(UniqY, HOInstInfoX),
- Res = yes(InstY)
+ Res = ok(InstY)
)
;
InstX = not_reached,
InstY = not_reached,
- Res = yes(InstY)
+ Res = ok(InstY)
;
InstX = inst_var(_),
unexpected($pred, "uninstantiated inst parameter")
@@ -242,15 +253,15 @@ modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, TypeX, TypeY,
% The input term of TypeX is approximated by a ground inst SubInstX.
% After conversion, the result term of TypeY must be approximated by
% a ground inst SubInstY.
- modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, TypeX, TypeY,
- ExpandedInsts0, SubInstX, SubRes),
+ modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, RevTermPath0,
+ TypeX, TypeY, ExpandedInsts0, SubInstX, SubRes),
(
- SubRes = yes(SubInstY),
+ SubRes = ok(SubInstY),
InstY = constrained_inst_vars(InstVars, SubInstY),
- Res = yes(InstY)
+ Res = ok(InstY)
;
- SubRes = no,
- Res = no
+ SubRes = coerce_error(Error),
+ Res = coerce_error(Error)
)
;
InstX = defined_inst(InstNameX),
@@ -261,24 +272,28 @@ modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, TypeX, TypeY,
then
inst_lookup(ModuleInfo, InstNameX, InstX1),
modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX,
- TypeX, TypeY, ExpandedInsts1, InstX1, Res)
+ RevTermPath0, TypeX, TypeY, ExpandedInsts1, InstX1, Res)
else
% If we would enter an infinite loop, return the inst unexpanded.
% A recursive check, by definition, cannot find any errors that the
% nonrecursive part of the algorithm would not find.
%
- % Just in case, ensure that we are definitely dealing with
- % user-defined inst; and TypeX =< TypeY so an inst valid for
- % TypeX must be valid for TypeY.
- % The sanity checks might not be strictly necessary.
- ( if
- is_user_inst(InstNameX),
- check_is_subtype(ModuleInfo, TVarSet, TypeX, TypeY)
- then
- InstY = defined_inst(InstNameX),
- Res = yes(InstY)
+ % Ensure that we are definitely dealing with user-defined inst.
+ % This check might not be strictly necessary.
+ ( if is_user_inst(InstNameX) then
+ % If TypeX =< TypeY then an inst valid for TypeX must be
+ % valid for TypeY.
+ ( if check_is_subtype(ModuleInfo, TVarSet, TypeX, TypeY) then
+ InstY = defined_inst(InstNameX),
+ Res = ok(InstY)
+ else
+ list.reverse(RevTermPath0, TermPath),
+ Reason = has_inst_expect_upcast(InstX),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ Res = coerce_error(Error)
+ )
else
- Res = no
+ unexpected($pred, "not user-defined inst")
)
)
;
@@ -339,82 +354,138 @@ is_user_inst(InstName) :-
% if the conversion is valid.
%
:- pred modecheck_coerce_from_bound_make_bound_inst(module_info::in,
- tvarset::in, is_live::in, uniqueness::in, mer_type::in, mer_type::in,
- expanded_insts::in, list(bound_inst)::in, maybe(mer_inst)::out) is det.
+ tvarset::in, is_live::in, uniqueness::in, rev_term_path::in,
+ mer_type::in, mer_type::in, expanded_insts::in, mer_inst::in,
+ list(bound_inst)::in, maybe_coerce_error(mer_inst)::out) is det.
modecheck_coerce_from_bound_make_bound_inst(ModuleInfo, TVarSet, LiveX, UniqX,
- TypeX, TypeY, ExpandedInsts0, FunctorsX, Res) :-
- ( if
- modecheck_coerce_from_bound_make_bound_functors(ModuleInfo, TVarSet,
- LiveX, TypeX, TypeY, ExpandedInsts0, FunctorsX, FunctorsY)
- then
- UniqY = uniqueness_for_coerce_result(LiveX, UniqX),
- % XXX A better approximation of InstResults is probably possible.
- InstResults = inst_test_results(
- inst_result_is_ground,
- inst_result_does_not_contain_any,
- inst_result_contains_inst_names_unknown,
- inst_result_contains_inst_vars_unknown,
- inst_result_contains_types_unknown,
- inst_result_no_type_ctor_propagated
- ),
- InstY = bound(UniqY, InstResults, FunctorsY),
- Res = yes(InstY)
- else
- Res = no
+ RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX, FunctorsX, Res) :-
+ modecheck_coerce_from_bound_make_bound_functors(ModuleInfo, TVarSet, LiveX,
+ RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX,
+ FunctorsX, FunctorsY, BadConsIds, no, MaybeError0),
+ (
+ BadConsIds = [],
+ (
+ MaybeError0 = no,
+ UniqY = uniqueness_for_coerce_result(LiveX, UniqX),
+ % XXX A better approximation of InstResults is probably possible.
+ InstResults = inst_test_results(
+ inst_result_is_ground,
+ inst_result_does_not_contain_any,
+ inst_result_contains_inst_names_unknown,
+ inst_result_contains_inst_vars_unknown,
+ inst_result_contains_types_unknown,
+ inst_result_no_type_ctor_propagated
+ ),
+ InstY = bound(UniqY, InstResults, FunctorsY),
+ Res = ok(InstY)
+ ;
+ MaybeError0 = yes(Error),
+ Res = coerce_error(Error)
+ )
+ ;
+ BadConsIds = [_ | _],
+ % This will prefer to report any invalid functors in the current
+ % `bound()' inst over an error found deeper in the inst tree
+ % (in MaybeError0).
+ list.reverse(RevTermPath0, TermPath),
+ Reason = invalid_cons_ids_for_result_type(BadConsIds),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ Res = coerce_error(Error)
).
- % XXX try to report which functors cause the coercion to be invalid
- %
:- pred modecheck_coerce_from_bound_make_bound_functors(module_info::in,
- tvarset::in, is_live::in, mer_type::in, mer_type::in, expanded_insts::in,
- list(bound_inst)::in, list(bound_inst)::out) is semidet.
+ tvarset::in, is_live::in, rev_term_path::in, mer_type::in, mer_type::in,
+ expanded_insts::in, mer_inst::in,
+ list(bound_inst)::in, list(bound_inst)::out, list(cons_id)::out,
+ maybe(coerce_error)::in, maybe(coerce_error)::out) is det.
modecheck_coerce_from_bound_make_bound_functors(ModuleInfo, TVarSet, LiveX,
- TypeX, TypeY, ExpandedInsts0, FunctorsX, FunctorsY) :-
+ RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX,
+ FunctorsX, FunctorsY, BadConsIds, !MaybeError) :-
(
FunctorsX = [],
- FunctorsY = []
+ FunctorsY = [],
+ BadConsIds = []
;
FunctorsX = [HeadFunctorX | TailFunctorsX],
modecheck_coerce_from_bound_make_bound_functor(ModuleInfo, TVarSet,
- LiveX, TypeX, TypeY, ExpandedInsts0,
- HeadFunctorX, yes(HeadFunctorY)),
+ LiveX, RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX,
+ HeadFunctorX, MaybeHeadFunctorY, !MaybeError),
+ % Check remaining functors in this `bound()' inst so that we can
+ % report multiple invalid functors together.
modecheck_coerce_from_bound_make_bound_functors(ModuleInfo, TVarSet,
- LiveX, TypeX, TypeY, ExpandedInsts0,
- TailFunctorsX, TailFunctorsY),
- FunctorsY = [HeadFunctorY | TailFunctorsY]
+ LiveX, RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX,
+ TailFunctorsX, TailFunctorsY, TailBadConsIds, !MaybeError),
+ (
+ MaybeHeadFunctorY = ok(HeadFunctorY),
+ FunctorsY = [HeadFunctorY | TailFunctorsY],
+ BadConsIds = TailBadConsIds
+ ;
+ MaybeHeadFunctorY = bad_cons_id(HeadBadConsId),
+ FunctorsY = TailFunctorsY,
+ BadConsIds = [HeadBadConsId | TailBadConsIds]
+ ;
+ MaybeHeadFunctorY = other_error,
+ FunctorsY = TailFunctorsY,
+ BadConsIds = TailBadConsIds
+ )
).
+:- type bound_inst_or_error
+ ---> ok(bound_inst)
+ ; bad_cons_id(cons_id)
+ ; other_error. % error kept separately
+
:- pred modecheck_coerce_from_bound_make_bound_functor(module_info::in,
- tvarset::in, is_live::in, mer_type::in, mer_type::in, expanded_insts::in,
- bound_inst::in, maybe(bound_inst)::out) is det.
+ tvarset::in, is_live::in, rev_term_path::in, mer_type::in, mer_type::in,
+ expanded_insts::in, mer_inst::in, bound_inst::in, bound_inst_or_error::out,
+ maybe(coerce_error)::in, maybe(coerce_error)::out) is det.
modecheck_coerce_from_bound_make_bound_functor(ModuleInfo, TVarSet, LiveX,
- TypeX, TypeY, ExpandedInsts0, FunctorX, Res) :-
+ RevTermPath0, TypeX, TypeY, ExpandedInsts0, InstX, FunctorX,
+ MaybeFunctorY, !MaybeError) :-
FunctorX = bound_functor(ConsIdX, ArgInstsX),
% The user may have provided an inst that does not make sense for the type.
% The compiler does not check for that elsewhere (probably it should)
% so we try to be careful about that here.
(
ConsIdX = cons(_, _, _),
- ( if
- get_bound_functor_cons_and_arg_types(ModuleInfo, TypeX, TypeY,
- ConsIdX, ConsIdY, ArgTypesX, ArgTypesY, Arity),
- list.length(ArgInstsX, Arity)
- then
- ( if
+ get_bound_functor_cons_and_arg_types(ModuleInfo, TypeX, TypeY,
+ ConsIdX, ConsIdY, GetArgTypesRes),
+ (
+ GetArgTypesRes = arg_types(ArgTypesX, ArgTypesY, Arity),
+ ( if list.length(ArgInstsX, Arity) then
modecheck_coerce_from_bound_make_bound_functor_arg_insts(
- ModuleInfo, TVarSet, LiveX, ExpandedInsts0,
- ArgTypesX, ArgTypesY, ArgInstsX, ArgInstsY)
- then
- FunctorY = bound_functor(ConsIdY, ArgInstsY),
- Res = yes(FunctorY)
+ ModuleInfo, TVarSet, LiveX, RevTermPath0, ExpandedInsts0,
+ ConsIdX, 1, ArgTypesX, ArgTypesY,
+ ArgInstsX, MaybeArgInstsY),
+ (
+ MaybeArgInstsY = ok(ArgInstsY),
+ FunctorY = bound_functor(ConsIdY, ArgInstsY),
+ MaybeFunctorY = ok(FunctorY)
+ ;
+ MaybeArgInstsY = coerce_error(Error),
+ maybe_keep_error(Error, !MaybeError),
+ MaybeFunctorY = other_error
+ )
else
- Res = no
+ list.reverse(RevTermPath0, TermPath),
+ Reason = invalid_inst_for_input_type(InstX),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ maybe_keep_error(Error, !MaybeError),
+ MaybeFunctorY = other_error
)
- else
- Res = no
+ ;
+ GetArgTypesRes = bad_cons_id_for_input_type,
+ list.reverse(RevTermPath0, TermPath),
+ Reason = invalid_inst_for_input_type(InstX),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ maybe_keep_error(Error, !MaybeError),
+ MaybeFunctorY = other_error
+ ;
+ GetArgTypesRes = bad_cons_id_for_result_type,
+ MaybeFunctorY = bad_cons_id(ConsIdY)
)
;
ConsIdX = tuple_cons(_),
@@ -436,15 +507,19 @@ modecheck_coerce_from_bound_make_bound_functor(ModuleInfo, TVarSet, LiveX,
; ConsIdX = char_const(_)
; ConsIdX = string_const(_)
),
- ( if
- cons_id_matches_builtin_type(ConsIdX, TypeX),
- TypeX = TypeY,
- ArgInstsX = []
- then
+ ( if cons_id_matches_builtin_type(ConsIdX, TypeX) then
+ expect(unify(TypeX, TypeY), $pred,
+ "coercion between different builtin types"),
+ expect(unify(ArgInstsX, []), $pred,
+ "bound functor literal has arguments"),
FunctorY = FunctorX,
- Res = yes(FunctorY)
+ MaybeFunctorY = ok(FunctorY)
else
- Res = no
+ list.reverse(RevTermPath0, TermPath),
+ Reason = invalid_inst_for_input_type(InstX),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ maybe_keep_error(Error, !MaybeError),
+ MaybeFunctorY = other_error
)
;
( ConsIdX = closure_cons(_, _)
@@ -463,84 +538,145 @@ modecheck_coerce_from_bound_make_bound_functor(ModuleInfo, TVarSet, LiveX,
unexpected($pred, "unsupported cons_id")
).
+:- type get_arg_types_result
+ ---> arg_types(list(mer_type), list(mer_type), int)
+ ; bad_cons_id_for_input_type
+ ; bad_cons_id_for_result_type.
+
:- pred get_bound_functor_cons_and_arg_types(module_info::in,
mer_type::in, mer_type::in, cons_id::in(cons), cons_id::out(cons),
- list(mer_type)::out, list(mer_type)::out, int::out) is semidet.
+ get_arg_types_result::out) is det.
get_bound_functor_cons_and_arg_types(ModuleInfo, TypeX, TypeY,
- ConsIdX, ConsIdY, ArgTypesX, ArgTypesY, Arity) :-
- type_to_ctor(TypeY, TypeCtorY),
+ ConsIdX, ConsIdY, Res) :-
+ type_to_ctor_det(TypeY, TypeCtorY),
make_cons_id_for_type_ctor(TypeCtorY, ConsIdX, ConsIdY),
- require_complete_switch [TypeX]
(
TypeX = defined_type(_, _, _),
- TypeY = defined_type(_, _, _),
- % This fails if the input type does not actually have the
- % functor given in a `bound' inst.
- get_ctor_arg_types_do_subst(ModuleInfo, TypeX, ConsIdX,
- ArgTypesX),
- % This fails if the result type does not have a constructor
- % matching that of the input type.
- get_ctor_arg_types_do_subst(ModuleInfo, TypeY, ConsIdY,
- ArgTypesY),
- list.length(ArgTypesX, Arity),
- list.length(ArgTypesY, Arity)
+ ( if
+ % This fails if the input type does not actually have the
+ % functor given in a `bound' inst.
+ get_ctor_arg_types_do_subst(ModuleInfo, TypeX, ConsIdX,
+ ArgTypesX)
+ then
+ ( if
+ TypeY = defined_type(_, _, _),
+ % This fails if the result type does not have a constructor
+ % matching that of the input type.
+ get_ctor_arg_types_do_subst(ModuleInfo, TypeY, ConsIdY,
+ ArgTypesY)
+ then
+ ( if
+ list.length(ArgTypesX, Arity),
+ list.length(ArgTypesY, Arity)
+ then
+ Res = arg_types(ArgTypesX, ArgTypesY, Arity)
+ else
+ unexpected($pred, "arg types length mismatch")
+ )
+ else
+ Res = bad_cons_id_for_result_type
+ )
+ else
+ Res = bad_cons_id_for_input_type
+ )
;
TypeX = tuple_type(ArgTypesX, _),
- TypeY = tuple_type(ArgTypesY, _),
- ConsIdX = cons(unqualified("{}"), Arity, _),
- list.length(ArgTypesX, Arity),
- list.length(ArgTypesY, Arity)
+ ( if
+ ConsIdX = cons(unqualified("{}"), Arity, _),
+ list.length(ArgTypesX, Arity)
+ then
+ ( if
+ TypeY = tuple_type(ArgTypesY, _),
+ list.length(ArgTypesY, Arity)
+ then
+ Res = arg_types(ArgTypesX, ArgTypesY, Arity)
+ else
+ unexpected($pred, "tuple type mismatch")
+ )
+ else
+ Res = bad_cons_id_for_input_type
+ )
;
TypeX = builtin_type(BuiltinType),
- TypeY = builtin_type(BuiltinType),
- % `cons' is used for char.
- BuiltinType = builtin_type_char,
- ConsIdX = cons(_SymName, Arity, _),
- Arity = 0,
- ArgTypesX = [],
- ArgTypesY = []
+ expect(unify(TypeX, TypeY), $pred,
+ "coercion between different builtin types"),
+ (
+ BuiltinType = builtin_type_char,
+ ConsIdX = cons(_SymName, Arity, _),
+ ( if Arity = 0 then
+ ArgTypesX = [],
+ ArgTypesY = [],
+ Res = arg_types(ArgTypesX, ArgTypesY, Arity)
+ else
+ Res = bad_cons_id_for_input_type
+ )
+ ;
+ ( BuiltinType = builtin_type_int(_)
+ ; BuiltinType = builtin_type_float
+ ; BuiltinType = builtin_type_string
+ ),
+ Res = bad_cons_id_for_input_type
+ )
;
TypeX = kinded_type(TypeX1, Kind),
- TypeY = kinded_type(TypeY1, Kind),
- get_bound_functor_cons_and_arg_types(ModuleInfo, TypeX1, TypeY1,
- ConsIdX, ConsIdY, ArgTypesX, ArgTypesY, Arity)
+ ( if TypeY = kinded_type(TypeY1, Kind) then
+ get_bound_functor_cons_and_arg_types(ModuleInfo, TypeX1, TypeY1,
+ ConsIdX, _ConsIdY, Res)
+ else
+ sorry($pred, "kinded type mismatch")
+ )
;
( TypeX = type_variable(_, _)
; TypeX = higher_order_type(_, _, _, _, _)
; TypeX = apply_n_type(_, _, _)
),
- fail
+ Res = bad_cons_id_for_input_type
).
- % XXX try to report which arguments cause the coercion to be invalid
- %
:- pred modecheck_coerce_from_bound_make_bound_functor_arg_insts(
- module_info::in, tvarset::in, is_live::in, expanded_insts::in,
+ module_info::in, tvarset::in, is_live::in, rev_term_path::in,
+ expanded_insts::in, cons_id::in, int::in,
list(mer_type)::in, list(mer_type)::in,
- list(mer_inst)::in, list(mer_inst)::out) is semidet.
+ list(mer_inst)::in, maybe_coerce_error(list(mer_inst))::out) is det.
modecheck_coerce_from_bound_make_bound_functor_arg_insts(ModuleInfo, TVarSet,
- LiveX, ExpandedInsts0, ArgTypesX, ArgTypesY, ArgInstsX, ArgInstsY) :-
+ LiveX, RevTermPath0, ExpandedInsts0, ConsIdX, CurArgNum,
+ ArgTypesX, ArgTypesY, ArgInstsX, MaybeArgInstsY) :-
( if
ArgTypesX = [],
ArgTypesY = [],
ArgInstsX = []
then
- ArgInstsY = []
+ MaybeArgInstsY = ok([])
else if
ArgTypesX = [HeadArgTypeX | TailArgTypesX],
ArgTypesY = [HeadArgTypeY | TailArgTypesY],
ArgInstsX = [HeadArgInstX | TailArgInstsX]
then
- modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX,
+ Step = coerce_error_term_path_step(ConsIdX, CurArgNum),
+ RevTermPath1 = [Step | RevTermPath0],
+ modecheck_coerce_make_inst(ModuleInfo, TVarSet, LiveX, RevTermPath1,
HeadArgTypeX, HeadArgTypeY, ExpandedInsts0,
- HeadArgInstX, yes(HeadArgInstY)),
- modecheck_coerce_from_bound_make_bound_functor_arg_insts(ModuleInfo,
- TVarSet, LiveX, ExpandedInsts0,
- TailArgTypesX, TailArgTypesY,
- TailArgInstsX, TailArgInstsY),
- ArgInstsY = [HeadArgInstY | TailArgInstsY]
+ HeadArgInstX, MaybeHeadArgInstY),
+ (
+ MaybeHeadArgInstY = ok(HeadArgInstY),
+ modecheck_coerce_from_bound_make_bound_functor_arg_insts(ModuleInfo,
+ TVarSet, LiveX, RevTermPath0, ExpandedInsts0,
+ ConsIdX, CurArgNum + 1,
+ TailArgTypesX, TailArgTypesY,
+ TailArgInstsX, MaybeTailArgInstsY),
+ (
+ MaybeTailArgInstsY = ok(TailArgInstsY),
+ MaybeArgInstsY = ok([HeadArgInstY | TailArgInstsY])
+ ;
+ MaybeTailArgInstsY = coerce_error(Error),
+ MaybeArgInstsY = coerce_error(Error)
+ )
+ ;
+ MaybeHeadArgInstY = coerce_error(Error),
+ MaybeArgInstsY = coerce_error(Error)
+ )
else
unexpected($pred, "length mismatch")
).
@@ -563,6 +699,20 @@ cons_id_matches_builtin_type(ConsId, Type) :-
; ConsId = string_const(_), Type = string_type
).
+:- pred maybe_keep_error(coerce_error::in,
+ maybe(coerce_error)::in, maybe(coerce_error)::out) is det.
+
+maybe_keep_error(Error, MaybeError0, MaybeError) :-
+ (
+ MaybeError0 = no,
+ MaybeError = yes(Error)
+ ;
+ MaybeError0 = yes(_Error0),
+ % We only keep one error as only one error message will be reported
+ % for a coerce expression.
+ MaybeError = MaybeError0
+ ).
+
%---------------------------------------------------------------------------%
% "from_ground" refers to the fact that the input term has the inst
@@ -570,23 +720,26 @@ cons_id_matches_builtin_type(ConsId, Type) :-
% conversion is valid.
%
:- pred modecheck_coerce_from_ground_make_inst(module_info::in, tvarset::in,
- is_live::in, uniqueness::in, mer_type::in, mer_type::in,
- maybe(mer_inst)::out) is det.
+ is_live::in, uniqueness::in, rev_term_path::in, mer_type::in, mer_type::in,
+ mer_inst::in, maybe_coerce_error(mer_inst)::out) is det.
modecheck_coerce_from_ground_make_inst(ModuleInfo, TVarSet, LiveX, UniqX,
- TypeX, TypeY, MaybeInstY) :-
+ RevTermPath0, TypeX, TypeY, InstX, MaybeInstY) :-
( if TypeX = TypeY then
% This should be a common case.
UniqY = uniqueness_for_coerce_result(LiveX, UniqX),
InstY = ground(UniqY, none_or_default_func),
- MaybeInstY = yes(InstY)
+ MaybeInstY = ok(InstY)
else if check_is_subtype(ModuleInfo, TVarSet, TypeX, TypeY) then
set.init(SeenTypes0),
modecheck_coerce_from_ground_make_inst_for_subtype(ModuleInfo, TVarSet,
LiveX, UniqX, SeenTypes0, TypeX, TypeY, InstY),
- MaybeInstY = yes(InstY)
+ MaybeInstY = ok(InstY)
else
- MaybeInstY = no
+ list.reverse(RevTermPath0, TermPath),
+ Reason = has_inst_expect_upcast(InstX),
+ Error = coerce_error(TermPath, TypeX, TypeY, Reason),
+ MaybeInstY = coerce_error(Error)
).
% Precondition: TypeX =< TypeY.
diff --git a/tests/invalid/Mmakefile b/tests/invalid/Mmakefile
index 16b917ec4..361c21c3a 100644
--- a/tests/invalid/Mmakefile
+++ b/tests/invalid/Mmakefile
@@ -125,6 +125,7 @@ SINGLEMODULE= \
coerce_instvar \
coerce_int \
coerce_mode_error \
+ coerce_mode_error2 \
coerce_non_du \
coerce_recursive_inst \
coerce_recursive_type \
diff --git a/tests/invalid/coerce_clobbered.err_exp b/tests/invalid/coerce_clobbered.err_exp
index 2bc874620..fd7140fa4 100644
--- a/tests/invalid/coerce_clobbered.err_exp
+++ b/tests/invalid/coerce_clobbered.err_exp
@@ -1,4 +1,4 @@
coerce_clobbered.m:021: In clause for `bad(in(clobbered), out)':
coerce_clobbered.m:021: in coerce:
-coerce_clobbered.m:021: mode error: `X' has instantiatedness `clobbered',
-coerce_clobbered.m:021: but it must be ground.
+coerce_clobbered.m:021: mode error: the input term has instantiatedness
+coerce_clobbered.m:021: `clobbered', but it must be ground.
diff --git a/tests/invalid/coerce_instvar.err_exp b/tests/invalid/coerce_instvar.err_exp
index 292f9a29d..0cb0e84a7 100644
--- a/tests/invalid/coerce_instvar.err_exp
+++ b/tests/invalid/coerce_instvar.err_exp
@@ -1,7 +1,7 @@
coerce_instvar.m:043: In clause for `bad(in((I =< ground)), out((I =<
coerce_instvar.m:043: ground)))':
coerce_instvar.m:043: in coerce:
-coerce_instvar.m:043: mode error: the input term has instantiatedness
-coerce_instvar.m:043: `( I =< ground )',
-coerce_instvar.m:043: and cannot be converted to the type
-coerce_instvar.m:043: `coerce_instvar.citrus'.
+coerce_instvar.m:043: mode error: cannot convert the input term from type
+coerce_instvar.m:043: `coerce_instvar.fruit' to `coerce_instvar.citrus'
+coerce_instvar.m:043: because it has instantiatedness `ground', and the
+coerce_instvar.m:043: former type is not a subtype of the latter type.
diff --git a/tests/invalid/coerce_int.err_exp b/tests/invalid/coerce_int.err_exp
index 197c8bc8d..a93597419 100644
--- a/tests/invalid/coerce_int.err_exp
+++ b/tests/invalid/coerce_int.err_exp
@@ -24,23 +24,7 @@ coerce_int.m:026: ).
coerce_int.m:032: In clause for `bad_wrong_type(in(coerce_int.wrap(bound(1 ;
coerce_int.m:032: 2)))) = out(coerce_int.wrap(bound(1 ; 2)))':
coerce_int.m:032: in coerce:
-coerce_int.m:032: mode error: the input term has instantiatedness
-coerce_int.m:032: named inst wrap(
-coerce_int.m:032: bound(
-coerce_int.m:032: 1
-coerce_int.m:032: ;
-coerce_int.m:032: 2
-coerce_int.m:032: )
-coerce_int.m:032: ),
-coerce_int.m:032: which expands to
-coerce_int.m:032: bound(
-coerce_int.m:032: wrap(
-coerce_int.m:032: bound(
-coerce_int.m:032: 1
-coerce_int.m:032: ;
-coerce_int.m:032: 2
-coerce_int.m:032: )
-coerce_int.m:032: )
-coerce_int.m:032: ),
-coerce_int.m:032: and cannot be converted to the type
-coerce_int.m:032: `coerce_int.wrap(uint)'.
+coerce_int.m:032: mode error: in the input term:
+coerce_int.m:032: in the argument of function symbol `wrap':
+coerce_int.m:032: the subterm has instantiatedness `bound(1 ; 2)', which is
+coerce_int.m:032: invalid for the type `uint'.
diff --git a/tests/invalid/coerce_mode_error.err_exp b/tests/invalid/coerce_mode_error.err_exp
index 0899016bb..46f750ea0 100644
--- a/tests/invalid/coerce_mode_error.err_exp
+++ b/tests/invalid/coerce_mode_error.err_exp
@@ -1,28 +1,37 @@
coerce_mode_error.m:035: In clause for `bad_coerce_free_input(in(free), out)':
coerce_mode_error.m:035: in coerce:
-coerce_mode_error.m:035: mode error: `X' has instantiatedness `free',
-coerce_mode_error.m:035: but it must be ground.
+coerce_mode_error.m:035: mode error: the input term has instantiatedness
+coerce_mode_error.m:035: `free', but it must be ground.
coerce_mode_error.m:046: In clause for `bad_fruit_to_citrus(in, out)':
coerce_mode_error.m:046: in coerce:
-coerce_mode_error.m:046: mode error: the input term has instantiatedness
-coerce_mode_error.m:046: `ground',
-coerce_mode_error.m:046: and cannot be converted to the type
-coerce_mode_error.m:046: `coerce_mode_error.citrus'.
+coerce_mode_error.m:046: mode error: cannot convert the input term from type
+coerce_mode_error.m:046: `coerce_mode_error.fruit' to
+coerce_mode_error.m:046: `coerce_mode_error.citrus' because it has
+coerce_mode_error.m:046: instantiatedness `ground', and the former type is
+coerce_mode_error.m:046: not a subtype of the latter type.
coerce_mode_error.m:052: In clause for `bad_nelist_f_to_list_c(in, out)':
coerce_mode_error.m:052: in coerce:
-coerce_mode_error.m:052: mode error: the input term has instantiatedness
-coerce_mode_error.m:052: `ground',
-coerce_mode_error.m:052: and cannot be converted to the type
-coerce_mode_error.m:052: `coerce_mode_error.list(coerce_mode_error.citrus)'.
+coerce_mode_error.m:052: mode error: cannot convert the input term from type
+coerce_mode_error.m:052: `coerce_mode_error.non_empty_list(coerce_mode_error.fruit)'
+coerce_mode_error.m:052: to
+coerce_mode_error.m:052: `coerce_mode_error.list(coerce_mode_error.citrus)'
+coerce_mode_error.m:052: because it has instantiatedness `ground', and the
+coerce_mode_error.m:052: former type is not a subtype of the latter type.
coerce_mode_error.m:064: In clause for `bad_from_list_to_least2(in, out)':
coerce_mode_error.m:064: in coerce:
-coerce_mode_error.m:064: mode error: the input term has instantiatedness
-coerce_mode_error.m:064: `bound('[|]'(ground, ground))',
-coerce_mode_error.m:064: and cannot be converted to the type
-coerce_mode_error.m:064: `coerce_mode_error.least2(V_1)'.
+coerce_mode_error.m:064: mode error: in the input term:
+coerce_mode_error.m:064: in argument 2 of function symbol `'[|]'':
+coerce_mode_error.m:064: cannot convert the subterm from type
+coerce_mode_error.m:064: `coerce_mode_error.list(V_1)' to
+coerce_mode_error.m:064: `coerce_mode_error.non_empty_list(V_1)' because it
+coerce_mode_error.m:064: has instantiatedness `ground', and the former type
+coerce_mode_error.m:064: is not a subtype of the latter type.
coerce_mode_error.m:073: In clause for `from_list_to_least2(in, out)':
coerce_mode_error.m:073: in coerce:
-coerce_mode_error.m:073: mode error: the input term has instantiatedness
-coerce_mode_error.m:073: `bound('[|]'(ground, ground))',
-coerce_mode_error.m:073: and cannot be converted to the type
-coerce_mode_error.m:073: `coerce_mode_error.least2(V_1)'.
+coerce_mode_error.m:073: mode error: in the input term:
+coerce_mode_error.m:073: in argument 2 of function symbol `'[|]'':
+coerce_mode_error.m:073: cannot convert the subterm from type
+coerce_mode_error.m:073: `coerce_mode_error.list(V_1)' to
+coerce_mode_error.m:073: `coerce_mode_error.non_empty_list(V_1)' because it
+coerce_mode_error.m:073: has instantiatedness `ground', and the former type
+coerce_mode_error.m:073: is not a subtype of the latter type.
diff --git a/tests/invalid/coerce_mode_error2.err_exp b/tests/invalid/coerce_mode_error2.err_exp
new file mode 100644
index 000000000..22a743f05
--- /dev/null
+++ b/tests/invalid/coerce_mode_error2.err_exp
@@ -0,0 +1,16 @@
+coerce_mode_error2.m:039: In clause for `test1(out)':
+coerce_mode_error2.m:039: in coerce:
+coerce_mode_error2.m:039: mode error: in the input term:
+coerce_mode_error2.m:039: in argument 2 of function symbol `foo':
+coerce_mode_error2.m:039: cannot convert the subterm from type
+coerce_mode_error2.m:039: `coerce_mode_error2.fruit' to
+coerce_mode_error2.m:039: `coerce_mode_error2.citrus' because its
+coerce_mode_error2.m:039: instantiatedness includes the function symbols
+coerce_mode_error2.m:039: `apple' and `banana'.
+coerce_mode_error2.m:050: In clause for `test2(out)':
+coerce_mode_error2.m:050: in coerce:
+coerce_mode_error2.m:050: mode error: cannot convert the input term from type
+coerce_mode_error2.m:050: `coerce_mode_error2.foo(coerce_mode_error2.fruit)'
+coerce_mode_error2.m:050: to `coerce_mode_error2.foo_citrus' because its
+coerce_mode_error2.m:050: instantiatedness includes the function symbol
+coerce_mode_error2.m:050: `nil'.
diff --git a/tests/invalid/coerce_mode_error2.m b/tests/invalid/coerce_mode_error2.m
new file mode 100644
index 000000000..b2c8dfda8
--- /dev/null
+++ b/tests/invalid/coerce_mode_error2.m
@@ -0,0 +1,52 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module coerce_mode_error2.
+:- interface.
+
+:- type fruit
+ ---> apple
+ ; orange
+ ; lemon
+ ; banana.
+
+:- type citrus =< fruit
+ ---> orange
+ ; lemon.
+
+:- type foo(T)
+ ---> nil
+ ; foo(int, T).
+
+:- type foo_citrus =< foo(citrus)
+ ---> foo(int, citrus).
+
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- pred test1(foo_citrus::out) is multi.
+
+test1(Y) :-
+ (
+ X = foo(1, apple) % apple cannot be converted
+ ;
+ X = foo(2, banana) % banana cannot be converted
+ ;
+ X = foo(3, orange)
+ ),
+ Y = coerce(X).
+
+:- pred test2(foo_citrus::out) is multi.
+
+test2(Y) :-
+ (
+ X = foo(1, apple) % apple cannot be converted
+ ;
+ X = nil % nil cannot be converted either
+ % and will be reported preferentially
+ ),
+ Y = coerce(X).
+
+%---------------------------------------------------------------------------%
diff --git a/tests/invalid/coerce_recursive_inst.err_exp b/tests/invalid/coerce_recursive_inst.err_exp
index 7b279c40b..718fc3598 100644
--- a/tests/invalid/coerce_recursive_inst.err_exp
+++ b/tests/invalid/coerce_recursive_inst.err_exp
@@ -2,46 +2,20 @@ coerce_recursive_inst.m:042: In clause for
coerce_recursive_inst.m:042: `bad1(in((coerce_recursive_inst.cons))) =
coerce_recursive_inst.m:042: out((coerce_recursive_inst.cons))':
coerce_recursive_inst.m:042: in coerce:
-coerce_recursive_inst.m:042: mode error: the input term has instantiatedness
-coerce_recursive_inst.m:042: named inst cons,
-coerce_recursive_inst.m:042: which expands to
-coerce_recursive_inst.m:042: bound(
-coerce_recursive_inst.m:042: cons(
-coerce_recursive_inst.m:042: ground,
-coerce_recursive_inst.m:042: named inst rec,
-coerce_recursive_inst.m:042: which expands to
-coerce_recursive_inst.m:042: bound(
-coerce_recursive_inst.m:042: cons(
-coerce_recursive_inst.m:042: ground,
-coerce_recursive_inst.m:042: named inst rec
-coerce_recursive_inst.m:042: )
-coerce_recursive_inst.m:042: ;
-coerce_recursive_inst.m:042: nil
-coerce_recursive_inst.m:042: )
-coerce_recursive_inst.m:042: )
-coerce_recursive_inst.m:042: ),
-coerce_recursive_inst.m:042: and cannot be converted to the type
-coerce_recursive_inst.m:042: `coerce_recursive_inst.rec(coerce_recursive_inst.citrus)'.
+coerce_recursive_inst.m:042: mode error: in the input term:
+coerce_recursive_inst.m:042: in argument 1 of function symbol `cons':
+coerce_recursive_inst.m:042: cannot convert the subterm from type
+coerce_recursive_inst.m:042: `coerce_recursive_inst.fruit' to
+coerce_recursive_inst.m:042: `coerce_recursive_inst.citrus' because it has
+coerce_recursive_inst.m:042: instantiatedness `ground', and the former type
+coerce_recursive_inst.m:042: is not a subtype of the latter type.
coerce_recursive_inst.m:048: In clause for
coerce_recursive_inst.m:048: `bad2(in((coerce_recursive_inst.cons))) = out':
coerce_recursive_inst.m:048: in coerce:
-coerce_recursive_inst.m:048: mode error: the input term has instantiatedness
-coerce_recursive_inst.m:048: named inst cons,
-coerce_recursive_inst.m:048: which expands to
-coerce_recursive_inst.m:048: bound(
-coerce_recursive_inst.m:048: cons(
-coerce_recursive_inst.m:048: ground,
-coerce_recursive_inst.m:048: named inst rec,
-coerce_recursive_inst.m:048: which expands to
-coerce_recursive_inst.m:048: bound(
-coerce_recursive_inst.m:048: cons(
-coerce_recursive_inst.m:048: ground,
-coerce_recursive_inst.m:048: named inst rec
-coerce_recursive_inst.m:048: )
-coerce_recursive_inst.m:048: ;
-coerce_recursive_inst.m:048: nil
-coerce_recursive_inst.m:048: )
-coerce_recursive_inst.m:048: )
-coerce_recursive_inst.m:048: ),
-coerce_recursive_inst.m:048: and cannot be converted to the type
-coerce_recursive_inst.m:048: `coerce_recursive_inst.rec(coerce_recursive_inst.citrus)'.
+coerce_recursive_inst.m:048: mode error: in the input term:
+coerce_recursive_inst.m:048: in argument 1 of function symbol `cons':
+coerce_recursive_inst.m:048: cannot convert the subterm from type
+coerce_recursive_inst.m:048: `coerce_recursive_inst.fruit' to
+coerce_recursive_inst.m:048: `coerce_recursive_inst.citrus' because it has
+coerce_recursive_inst.m:048: instantiatedness `ground', and the former type
+coerce_recursive_inst.m:048: is not a subtype of the latter type.
diff --git a/tests/invalid/coerce_recursive_type.err_exp b/tests/invalid/coerce_recursive_type.err_exp
index 7cb64f357..bd90346aa 100644
--- a/tests/invalid/coerce_recursive_type.err_exp
+++ b/tests/invalid/coerce_recursive_type.err_exp
@@ -1,72 +1,32 @@
coerce_recursive_type.m:045: In clause for `bad(in) = out':
coerce_recursive_type.m:045: in coerce:
-coerce_recursive_type.m:045: mode error: the input term has instantiatedness
-coerce_recursive_type.m:045: `ground',
-coerce_recursive_type.m:045: and cannot be converted to the type
-coerce_recursive_type.m:045: `coerce_recursive_type.rec(coerce_recursive_type.citrus)'.
+coerce_recursive_type.m:045: mode error: cannot convert the input term from
+coerce_recursive_type.m:045: type
+coerce_recursive_type.m:045: `coerce_recursive_type.rec(coerce_recursive_type.fruit)'
+coerce_recursive_type.m:045: to
+coerce_recursive_type.m:045: `coerce_recursive_type.rec(coerce_recursive_type.citrus)'
+coerce_recursive_type.m:045: because it has instantiatedness `ground', and
+coerce_recursive_type.m:045: the former type is not a subtype of the latter
+coerce_recursive_type.m:045: type.
coerce_recursive_type.m:051: In clause for
coerce_recursive_type.m:051: `bad2(in((coerce_recursive_type.cons_apple3))) =
coerce_recursive_type.m:051: out':
coerce_recursive_type.m:051: in coerce:
-coerce_recursive_type.m:051: mode error: the input term has instantiatedness
-coerce_recursive_type.m:051: named inst cons_apple3,
-coerce_recursive_type.m:051: which expands to
-coerce_recursive_type.m:051: bound(
-coerce_recursive_type.m:051: cons(
-coerce_recursive_type.m:051: ground,
-coerce_recursive_type.m:051: named inst cons_apple2,
-coerce_recursive_type.m:051: which expands to
-coerce_recursive_type.m:051: bound(
-coerce_recursive_type.m:051: cons(
-coerce_recursive_type.m:051: ground,
-coerce_recursive_type.m:051: named inst cons_apple,
-coerce_recursive_type.m:051: which expands to
-coerce_recursive_type.m:051: bound(
-coerce_recursive_type.m:051: cons(
-coerce_recursive_type.m:051: named inst apple,
-coerce_recursive_type.m:051: which expands to
-coerce_recursive_type.m:051: bound(
-coerce_recursive_type.m:051: apple
-coerce_recursive_type.m:051: ),
-coerce_recursive_type.m:051: ground
-coerce_recursive_type.m:051: )
-coerce_recursive_type.m:051: )
-coerce_recursive_type.m:051: )
-coerce_recursive_type.m:051: )
-coerce_recursive_type.m:051: )
-coerce_recursive_type.m:051: ),
-coerce_recursive_type.m:051: and cannot be converted to the type
-coerce_recursive_type.m:051: `coerce_recursive_type.rec(coerce_recursive_type.citrus)'.
+coerce_recursive_type.m:051: mode error: in the input term:
+coerce_recursive_type.m:051: in argument 1 of function symbol `cons':
+coerce_recursive_type.m:051: cannot convert the subterm from type
+coerce_recursive_type.m:051: `coerce_recursive_type.fruit' to
+coerce_recursive_type.m:051: `coerce_recursive_type.citrus' because it has
+coerce_recursive_type.m:051: instantiatedness `ground', and the former type
+coerce_recursive_type.m:051: is not a subtype of the latter type.
coerce_recursive_type.m:058: In clause for
coerce_recursive_type.m:058: `bad3(in((coerce_recursive_type.cons_apple3))) =
coerce_recursive_type.m:058: out':
coerce_recursive_type.m:058: in coerce:
-coerce_recursive_type.m:058: mode error: the input term has instantiatedness
-coerce_recursive_type.m:058: named inst cons_apple3,
-coerce_recursive_type.m:058: which expands to
-coerce_recursive_type.m:058: bound(
-coerce_recursive_type.m:058: cons(
-coerce_recursive_type.m:058: ground,
-coerce_recursive_type.m:058: named inst cons_apple2,
-coerce_recursive_type.m:058: which expands to
-coerce_recursive_type.m:058: bound(
-coerce_recursive_type.m:058: cons(
-coerce_recursive_type.m:058: ground,
-coerce_recursive_type.m:058: named inst cons_apple,
-coerce_recursive_type.m:058: which expands to
-coerce_recursive_type.m:058: bound(
-coerce_recursive_type.m:058: cons(
-coerce_recursive_type.m:058: named inst apple,
-coerce_recursive_type.m:058: which expands to
-coerce_recursive_type.m:058: bound(
-coerce_recursive_type.m:058: apple
-coerce_recursive_type.m:058: ),
-coerce_recursive_type.m:058: ground
-coerce_recursive_type.m:058: )
-coerce_recursive_type.m:058: )
-coerce_recursive_type.m:058: )
-coerce_recursive_type.m:058: )
-coerce_recursive_type.m:058: )
-coerce_recursive_type.m:058: ),
-coerce_recursive_type.m:058: and cannot be converted to the type
-coerce_recursive_type.m:058: `coerce_recursive_type.rec(coerce_recursive_type.fruit)'.
+coerce_recursive_type.m:058: mode error: in the input term:
+coerce_recursive_type.m:058: in argument 2 of function symbol `cons':
+coerce_recursive_type.m:058: in argument 2 of function symbol `cons':
+coerce_recursive_type.m:058: in argument 1 of function symbol `cons':
+coerce_recursive_type.m:058: the subterm has instantiatedness `bound(apple)',
+coerce_recursive_type.m:058: which is invalid for the type
+coerce_recursive_type.m:058: `coerce_recursive_type.citrus'.
diff --git a/tests/invalid/coerce_unreachable.err_exp b/tests/invalid/coerce_unreachable.err_exp
index 559537314..ce04f78a5 100644
--- a/tests/invalid/coerce_unreachable.err_exp
+++ b/tests/invalid/coerce_unreachable.err_exp
@@ -1,7 +1,8 @@
coerce_unreachable.m:027: In clause for `p1(in, out)':
coerce_unreachable.m:027: in coerce:
-coerce_unreachable.m:027: mode error: the input term has instantiatedness
-coerce_unreachable.m:027: `ground',
-coerce_unreachable.m:027: and cannot be converted to the type
-coerce_unreachable.m:027: `coerce_unreachable.citrus'.
+coerce_unreachable.m:027: mode error: cannot convert the input term from type
+coerce_unreachable.m:027: `coerce_unreachable.fruit' to
+coerce_unreachable.m:027: `coerce_unreachable.citrus' because it has
+coerce_unreachable.m:027: instantiatedness `ground', and the former type is
+coerce_unreachable.m:027: not a subtype of the latter type.
For more information, recompile with `-E'.
--
2.30.0
More information about the reviews
mailing list