mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
compiler/var_origins.m:
The abort occurred when this module was given a predicate body
that included calls with unresolved callees. When trying to generate
a warning about a variable with an unresolved type that originates
in such a call, we tried to look up the name of the callee
by effectively dereferencing an invalid pred_id.
Fix this by
- recording for each call not just the pred_id of callee, but
also its sym name; and
- if that pred_id is invalid, then use the recorded sym_name
in the message instead. The sym_name must be always be
meaningful for user-written calls; for compiler-generated calls,
the sym_name does not matter (though it *should* be correct),
because for them, the pred_id will be valid.
compiler/post_typecheck.m:
Pass the extra information that var_origins.m now needs.
860 lines
36 KiB
Mathematica
860 lines
36 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This module provides mechanisms to
|
|
%
|
|
% - map each variable in a predicate to its earliest occurrence in each
|
|
% clause of a given predicate (its origins), and
|
|
%
|
|
% - to map descriptions of such origins into text that can be included
|
|
% in error messages.
|
|
%
|
|
% The overall intention is to improve the readability of error messages
|
|
% such as
|
|
%
|
|
% Warning: unresolved polymorphism in predicate `maybe_throw_pred'/2.
|
|
% The variable with an unbound type was:
|
|
% `V_7': exception.exception_result(T).
|
|
% The unbound type variable `T' will be implicitly bound to
|
|
% the builtin type `void'.
|
|
%
|
|
% Each of these lines has traditionally been prefixed by the context
|
|
% of the predicate as a whole. When the subject of the diagnostic is
|
|
% a named variable, this does not matter, since users can look for
|
|
% occurrences of that variable in the predicate's code. However, for
|
|
% unnamed variables, they cannot. This is why this module provides
|
|
% the mechanisms needed to extend the diagnostic with text such as
|
|
%
|
|
% `V_7' represents the term `exception(Exception)'.
|
|
%
|
|
% with the preceding context pinpointing the line where the unnamed
|
|
% variable first occurs. See tests/warnings/unresolved_polymorphism_anon.m.
|
|
%
|
|
% Because this intention is to help with error messages created during
|
|
% type analysis, this module does not look at, or use, any part of the HLDS
|
|
% that is not meaningful during type analysis, such as instmap_deltas.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module hlds.var_origins.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type record_var_origin(T)
|
|
== (pred(var_origins_map, prog_var, var_origin, T, T)).
|
|
:- inst record_var_origin % for record_var_origin/1
|
|
== (pred(in, in, in, in, out) is det).
|
|
|
|
% Values of this type record the location of the origin,
|
|
% i.e. the first occurrence, of a variable. This means
|
|
% location both in terms of filename/linenumber, and in terms of
|
|
% what part of what goal.
|
|
:- type var_origin
|
|
---> var_origin_clause_head(
|
|
% The variable's first occurrence is in the clause head.
|
|
voch_context :: prog_context,
|
|
|
|
% The number of the clause in the predicate's definition.
|
|
% The first clause is clause #1.
|
|
voch_clause :: uint,
|
|
|
|
% The position of the variable in the clause head.
|
|
% The first argument is argument #1.
|
|
voch_arg_num :: uint
|
|
)
|
|
; var_origin_lambda_head(
|
|
% The variable's first occurrence is in the head of
|
|
% a lambda expression.
|
|
volh_context :: prog_context,
|
|
volh_p_or_f :: pred_or_func,
|
|
volh_arg_num :: uint
|
|
)
|
|
; var_origin_unify_var(
|
|
vouv_context :: prog_context,
|
|
% The other variable that the variable mapped to this origin
|
|
% is unified with.
|
|
vouv_lorhs :: lhs_or_rhs
|
|
)
|
|
; var_origin_unify_func(
|
|
vouf_context :: prog_context,
|
|
|
|
% The cons_id in the unification.
|
|
vouf_cons_id :: cons_id,
|
|
|
|
% If the variable mapped to this origin is the LHS var,
|
|
% then this lists the RHS vars.
|
|
% If the variable mapped to this origin is one of the RHS vars,
|
|
% then this gives its position on the RHS, as well as
|
|
% the LHS var.
|
|
vouf_lorhsa :: lhs_or_rhs_arg
|
|
)
|
|
; var_origin_plain_call(
|
|
vopc_context :: prog_context,
|
|
% The id of the callee, and the number of the argument
|
|
% position that is occupied by the variable mapped
|
|
% to this origin. (If that variable is only a *component*
|
|
% of the argument, and not the whole argument, then its
|
|
% origin will be the unification introduced by the code of
|
|
% superhomoneous.m.)
|
|
%
|
|
% We include the sym_name of the callee, because if the
|
|
% callee's identity is unresolved, then the pred will be
|
|
% invalid.
|
|
vopc_callee_id :: pred_id,
|
|
vopc_callee_sn :: sym_name,
|
|
vopc_arg_num :: uint
|
|
)
|
|
; var_origin_foreign_call(
|
|
vofc_context :: prog_context,
|
|
% We treat foreign language calls the same way as plain calls.
|
|
vofc_callee_id :: pred_id,
|
|
vofc_callee_sn :: sym_name,
|
|
vofc_arg_num :: uint
|
|
)
|
|
; var_origin_generic_call(
|
|
vogc_context :: prog_context,
|
|
% We treat treat language calls similarly to plain calls,
|
|
% just with a different specification of the callee.
|
|
vogc_callee :: generic_call,
|
|
vogc_arg_num :: uint
|
|
).
|
|
|
|
:- type lhs_or_rhs
|
|
---> lor_lhs(
|
|
rhs_var :: prog_var
|
|
)
|
|
; lor_rhs(
|
|
lhs_var :: prog_var
|
|
).
|
|
|
|
:- type lhs_or_rhs_arg
|
|
---> lora_lhs(
|
|
rhs_argvars :: list(prog_var)
|
|
)
|
|
; lora_rhs(
|
|
lhs_var :: prog_var,
|
|
rhs_arg_num :: uint
|
|
).
|
|
|
|
% Values of this type map each variable to
|
|
%
|
|
% - its origin, if it has only one origin (that we have seen so far),
|
|
%
|
|
% - but if the variable has its origin in a (possibly nested) branched
|
|
% control structure, it will contain its origin on each branch.
|
|
% Note that multiple clauses, being equivalent to multiple disjuncts,
|
|
% count as branches of a branched control structure.
|
|
:- type var_origins_map == map(prog_var, set(var_origin)).
|
|
|
|
% compute_var_origins_in_pred(CollectPred, ModuleInfo, PredInfo0,
|
|
% OriginsMap, !Acc):
|
|
%
|
|
% Given a predicate, construct and return OriginsMap, which will map
|
|
% each variable in its clauses to its set of origins.
|
|
%
|
|
% Note that one kind of goal, negations, introduce scopes that 'forget'
|
|
% origins. This is because if a variable is generated in a negated goal,
|
|
% it cannot be referred to from outside the negation. Therefore any
|
|
% additions to OriginsMap inside a negated goal will be effectively
|
|
% forgotten when the traversal leaves the negation.
|
|
%
|
|
% This is why instead of letting our caller post-process OriginsMap,
|
|
% we allow it to process each addition to OriginsMap as it is made.
|
|
% The traversal will call CollectPred after each such addition,
|
|
% passing it both the already-updated OriginsMap, and the pair of
|
|
% <variable, origin> that was just added.
|
|
%
|
|
:- pred compute_var_origins_in_pred(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
module_info::in, pred_info::in, var_origins_map::out,
|
|
T::in, T::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% This predicate is intended to be invoked from the CollectPred
|
|
% passed to compute_var_origins_in_pred. Given the overall HLDS
|
|
% and the var_table of the predicate being processed, it returns
|
|
% a description of the variable and its origin. The output will
|
|
% look like this:
|
|
%
|
|
% <context>: `V_7' represents the term `exception(Exception)'.
|
|
%
|
|
:- pred explain_var_origin(module_info::in, var_table::in,
|
|
prog_var::in, var_origin::in, list(error_msg)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_util.
|
|
:- import_module parse_tree.parse_tree_out_cons_id.
|
|
:- import_module parse_tree.parse_tree_out_info.
|
|
:- import_module parse_tree.parse_tree_out_term.
|
|
:- import_module parse_tree.var_db.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module int.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module uint.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
compute_var_origins_in_pred(CollectPred, ModuleInfo, PredInfo0,
|
|
OriginsMap, !Acc) :-
|
|
pred_info_get_clauses_info(PredInfo0, ClausesInfo0),
|
|
clauses_info_get_headvar_list(ClausesInfo0, HeadVars),
|
|
clauses_info_get_clauses_rep(ClausesInfo0, ClausesRep0, _ItemNumbers),
|
|
get_clause_list(Clauses, ClausesRep0, _ClausesRep),
|
|
compute_var_origins_in_clauses(CollectPred, ModuleInfo, HeadVars,
|
|
1u, Clauses, [], RevOriginsMapList, !Acc),
|
|
list.reverse(RevOriginsMapList, OriginsMapList),
|
|
represent_origins_on_all_branches(OriginsMapList, OriginsMap).
|
|
|
|
:- pred compute_var_origins_in_clauses(
|
|
record_var_origin(T)::in(record_var_origin), module_info::in,
|
|
list(prog_var)::in, uint::in, list(clause)::in,
|
|
list(var_origins_map)::in, list(var_origins_map)::out,
|
|
T::in, T::out) is det.
|
|
|
|
compute_var_origins_in_clauses(_CollectPred, _ModuleInfo, _HeadVars,
|
|
_CurClauseNum, [], !RevOriginsMapList, !Acc).
|
|
compute_var_origins_in_clauses(CollectPred, ModuleInfo, HeadVars,
|
|
CurClauseNum, [Clause | Clauses], !RevOriginsMapList, !Acc) :-
|
|
Clause = clause(_ApplicableProcs, BodyGoal, Lang, Context,
|
|
_StateVarWarnings, _UnusedSVarDescs, _MaybeFact),
|
|
(
|
|
Lang = impl_lang_mercury,
|
|
OriginClauseHead = origin_clause_head(Context, CurClauseNum, HeadVars),
|
|
update_var_origins_clause_head(CollectPred, OriginClauseHead,
|
|
OriginsMap1, !Acc),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, BodyGoal,
|
|
OriginsMap1, OriginsMap, !Acc),
|
|
!:RevOriginsMapList = [OriginsMap | !.RevOriginsMapList],
|
|
NextClauseNum = CurClauseNum + 1u
|
|
;
|
|
Lang = impl_lang_foreign(_),
|
|
NextClauseNum = CurClauseNum
|
|
),
|
|
compute_var_origins_in_clauses(CollectPred, ModuleInfo, HeadVars,
|
|
NextClauseNum, Clauses, !RevOriginsMapList, !Acc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred compute_var_origins_in_goal(
|
|
record_var_origin(T)::in(record_var_origin), module_info::in,
|
|
hlds_goal::in, var_origins_map::in, var_origins_map::out,
|
|
T::in, T::out) is det.
|
|
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, Goal,
|
|
!OriginsMap, !Acc) :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
(
|
|
GoalExpr = unify(LHSVar, RHS, _, _, _),
|
|
(
|
|
RHS = rhs_var(RHSVar),
|
|
OriginUnifyVar = origin_unify_var(Context, LHSVar, RHSVar),
|
|
update_var_origins_unify_var(CollectPred, OriginUnifyVar,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
RHS = rhs_functor(ConsId, _IsExistConstr, RHSVars),
|
|
OriginUnifyFunc = origin_unify_func(Context,
|
|
LHSVar, ConsId, RHSVars),
|
|
update_var_origins_unify_func(CollectPred, OriginUnifyFunc,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
RHS = rhs_lambda_goal(_, _, PredOrFunc, _, VarsModes,
|
|
_, LambdaGoal),
|
|
OriginLambdaHead = origin_lambda_head(Context, PredOrFunc,
|
|
VarsModes),
|
|
update_var_origins_lambda_head(CollectPred, OriginLambdaHead,
|
|
!.OriginsMap, OriginsMapAfterLambdaHead, !Acc),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, LambdaGoal,
|
|
OriginsMapAfterLambdaHead, _OriginsMapAfterLambda, !Acc)
|
|
)
|
|
;
|
|
GoalExpr = plain_call(PredId, _, ArgVars, _, _, CalleeSymName),
|
|
OriginPlainCall = origin_plain_call(Context, PredId, CalleeSymName,
|
|
ArgVars),
|
|
update_var_origins_plain_call(CollectPred, OriginPlainCall,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
GoalExpr = call_foreign_proc(_, PredId, _, ArgVars, _, _, _),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_sym_name(PredInfo, CalleeSymName),
|
|
OriginForeignCall =
|
|
origin_foreign_call(Context, PredId, CalleeSymName, ArgVars),
|
|
update_var_origins_foreign_call(CollectPred, OriginForeignCall,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
GoalExpr = generic_call(GCall, ArgVars, _, _, _),
|
|
OriginGenericCall = origin_generic_call(Context, GCall, ArgVars),
|
|
update_var_origins_generic_call(CollectPred, OriginGenericCall,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
GoalExpr = conj(_ConjType, Conjuncts),
|
|
compute_var_origins_in_conj(CollectPred, ModuleInfo, Conjuncts,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
GoalExpr = disj(Disjuncts),
|
|
compute_var_origins_in_disj(CollectPred, ModuleInfo,
|
|
!.OriginsMap, Disjuncts, [], OriginsMaps, !Acc),
|
|
represent_origins_on_all_branches(OriginsMaps, !:OriginsMap)
|
|
;
|
|
GoalExpr = switch(_Var, _CanFail, Cases),
|
|
compute_var_origins_in_cases(CollectPred, ModuleInfo,
|
|
!.OriginsMap, Cases, [], OriginsMaps, !Acc),
|
|
represent_origins_on_all_branches(OriginsMaps, !:OriginsMap)
|
|
;
|
|
GoalExpr = negation(SubGoal),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, SubGoal,
|
|
!.OriginsMap, _OriginsMapAfterNegation, !Acc)
|
|
;
|
|
GoalExpr = scope(_, SubGoal),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, SubGoal,
|
|
!OriginsMap, !Acc)
|
|
;
|
|
GoalExpr = if_then_else(_, CondGoal, ThenGoal, ElseGoal),
|
|
OriginsMap0 = !.OriginsMap,
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, CondGoal,
|
|
OriginsMap0, OriginsMapAfterCond, !Acc),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, ThenGoal,
|
|
OriginsMapAfterCond, OriginsMapAfterThen, !Acc),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, ElseGoal,
|
|
OriginsMap0, OriginsMapAfterElse, !Acc),
|
|
OriginsMaps = [OriginsMapAfterThen, OriginsMapAfterElse],
|
|
represent_origins_on_all_branches(OriginsMaps, !:OriginsMap)
|
|
;
|
|
GoalExpr = shorthand(Shorthand),
|
|
(
|
|
Shorthand = bi_implication(_, _)
|
|
% These should have been expanded by now.
|
|
;
|
|
Shorthand = atomic_goal(_, _, _, _, _, _, _)
|
|
% These are not yet used.
|
|
;
|
|
Shorthand = try_goal(_, _, SubGoal),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, SubGoal,
|
|
!OriginsMap, !Acc)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred compute_var_origins_in_conj(
|
|
record_var_origin(T)::in(record_var_origin), module_info::in,
|
|
list(hlds_goal)::in, var_origins_map::in, var_origins_map::out,
|
|
T::in, T::out) is det.
|
|
|
|
compute_var_origins_in_conj(_CollectPred, _ModuleInfo, [], !OriginsMap, !Acc).
|
|
compute_var_origins_in_conj(CollectPred, ModuleInfo, [Conjunct | Conjuncts],
|
|
!OriginsMap, !Acc) :-
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, Conjunct,
|
|
!OriginsMap, !Acc),
|
|
compute_var_origins_in_conj(CollectPred, ModuleInfo, Conjuncts,
|
|
!OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred compute_var_origins_in_disj(
|
|
record_var_origin(T)::in(record_var_origin), module_info::in,
|
|
var_origins_map::in, list(hlds_goal)::in,
|
|
list(var_origins_map)::in, list(var_origins_map)::out,
|
|
T::in, T::out) is det.
|
|
|
|
compute_var_origins_in_disj(_CollectPred, _ModuleInfo, _InitialOriginsMap,
|
|
[], !RevOriginsMaps, !Acc).
|
|
compute_var_origins_in_disj(CollectPred, ModuleInfo, InitialOriginsMap,
|
|
[Disjunct | Disjuncts], !RevOriginsMaps, !Acc) :-
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, Disjunct,
|
|
InitialOriginsMap, DisjunctOriginsMap, !Acc),
|
|
!:RevOriginsMaps = [DisjunctOriginsMap | !.RevOriginsMaps],
|
|
compute_var_origins_in_disj(CollectPred, ModuleInfo, InitialOriginsMap,
|
|
Disjuncts, !RevOriginsMaps, !Acc).
|
|
|
|
:- pred compute_var_origins_in_cases(
|
|
record_var_origin(T)::in(record_var_origin), module_info::in,
|
|
var_origins_map::in, list(case)::in,
|
|
list(var_origins_map)::in, list(var_origins_map)::out,
|
|
T::in, T::out) is det.
|
|
|
|
compute_var_origins_in_cases(_CollectPred, _ModuleInfo, _InitialOriginsMap,
|
|
[], !RevOriginsMaps, !Acc).
|
|
compute_var_origins_in_cases(CollectPred, ModuleInfo, InitialOriginsMap,
|
|
[Case | Cases], !RevOriginsMaps, !Acc) :-
|
|
Case = case(_MainConsId, _OtherConsIds, SubGoal),
|
|
compute_var_origins_in_goal(CollectPred, ModuleInfo, SubGoal,
|
|
InitialOriginsMap, CaseOriginsMap, !Acc),
|
|
!:RevOriginsMaps = [CaseOriginsMap | !.RevOriginsMaps],
|
|
compute_var_origins_in_cases(CollectPred, ModuleInfo, InitialOriginsMap,
|
|
Cases, !RevOriginsMaps, !Acc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Descriptions of the various atomic goals that may contain the first
|
|
% occurrence of a variable.
|
|
%
|
|
% Each of the following subsections handles the recording of all the variable
|
|
% occurrences in each of these kinds of atomic goals.
|
|
%
|
|
|
|
:- type origin_clause_head
|
|
---> origin_clause_head(
|
|
och_context :: prog_context,
|
|
och_clause :: uint,
|
|
och_args :: list(prog_var)
|
|
).
|
|
|
|
:- type origin_lambda_head
|
|
---> origin_lambda_head(
|
|
olh_context :: prog_context,
|
|
olh_p_or_f :: pred_or_func,
|
|
olh_args :: assoc_list(prog_var, mer_mode)
|
|
).
|
|
|
|
:- type origin_unify_var
|
|
---> origin_unify_var(
|
|
ouv_context :: prog_context,
|
|
ouv_lhs :: prog_var,
|
|
ouv_rhs :: prog_var
|
|
).
|
|
|
|
:- type origin_unify_func
|
|
---> origin_unify_func(
|
|
ouf_context :: prog_context,
|
|
ouf_lhs :: prog_var,
|
|
ouf_cons_id :: cons_id,
|
|
ouf_rhs :: list(prog_var)
|
|
).
|
|
|
|
:- type origin_plain_call
|
|
---> origin_plain_call(
|
|
opc_context :: prog_context,
|
|
opc_callee_id :: pred_id,
|
|
opc_callee_sn :: sym_name,
|
|
opc_args :: list(prog_var)
|
|
).
|
|
|
|
:- type origin_foreign_call
|
|
---> origin_foreign_call(
|
|
ofc_context :: prog_context,
|
|
ofc_callee_id :: pred_id,
|
|
ofc_callee_sn :: sym_name,
|
|
ofc_args :: list(foreign_arg)
|
|
).
|
|
|
|
:- type origin_generic_call
|
|
---> origin_generic_call(
|
|
ogc_context :: prog_context,
|
|
ogc_callee :: generic_call,
|
|
ogc_args :: list(prog_var)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_clause_head(
|
|
record_var_origin(T)::in(record_var_origin), origin_clause_head::in,
|
|
var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_clause_head(CollectPred, OriginClauseHead,
|
|
!:OriginsMap, !Acc) :-
|
|
OriginClauseHead = origin_clause_head(Context, ClauseNum, ArgVars),
|
|
map.init(!:OriginsMap),
|
|
update_var_origins_clause_head_args(CollectPred, Context, ClauseNum,
|
|
1u, ArgVars, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_clause_head_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, uint::in, uint::in, list(prog_var)::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_clause_head_args(_CollectPred, _Context,
|
|
_CurClauseNum, _CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_clause_head_args(CollectPred, Context,
|
|
CurClauseNum, CurArgNum, [ArgVar | ArgVars], !OriginsMap, !Acc) :-
|
|
Origin = var_origin_clause_head(Context, CurClauseNum, CurArgNum),
|
|
update_var_origin(CollectPred, ArgVar, Origin, !OriginsMap, !Acc),
|
|
update_var_origins_clause_head_args(CollectPred, Context,
|
|
CurClauseNum, CurArgNum + 1u, ArgVars, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_lambda_head(
|
|
record_var_origin(T)::in(record_var_origin), origin_lambda_head::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_lambda_head(CollectPred, OriginLambdaHead,
|
|
!OriginsMap, !Acc) :-
|
|
OriginLambdaHead = origin_lambda_head(Context, PredOrFunc, ArgVarsModes),
|
|
update_var_origins_lambda_head_args(CollectPred, Context, PredOrFunc,
|
|
1u, ArgVarsModes, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_lambda_head_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, pred_or_func::in, uint::in,
|
|
assoc_list(prog_var, mer_mode)::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_lambda_head_args(_CollectPred, _Context, _PredOrFunc,
|
|
_CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_lambda_head_args(CollectPred, Context, PredOrFunc,
|
|
CurArgNum, [ArgVarMode | ArgVarsModes], !OriginsMap, !Acc) :-
|
|
ArgVarMode = ArgVar - _Mode,
|
|
Origin = var_origin_lambda_head(Context, PredOrFunc, CurArgNum),
|
|
update_var_origin(CollectPred, ArgVar, Origin, !OriginsMap, !Acc),
|
|
update_var_origins_lambda_head_args(CollectPred, Context, PredOrFunc,
|
|
CurArgNum + 1u, ArgVarsModes, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_unify_var(
|
|
record_var_origin(T)::in(record_var_origin), origin_unify_var::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_unify_var(CollectPred, OriginUnifyVar,
|
|
!OriginsMap, !Acc) :-
|
|
OriginUnifyVar = origin_unify_var(Context, LHSVar, RHSVar),
|
|
OriginLHS = var_origin_unify_var(Context, lor_lhs(RHSVar)),
|
|
OriginRHS = var_origin_unify_var(Context, lor_rhs(LHSVar)),
|
|
update_var_origin(CollectPred, LHSVar, OriginLHS, !OriginsMap, !Acc),
|
|
update_var_origin(CollectPred, RHSVar, OriginRHS, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_unify_func(
|
|
record_var_origin(T)::in(record_var_origin), origin_unify_func::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_unify_func(CollectPred, OriginUnifyFunc,
|
|
!OriginsMap, !Acc) :-
|
|
OriginUnifyFunc = origin_unify_func(Context, LHSVar, ConsId, RHSVars),
|
|
OriginLHS = var_origin_unify_func(Context, ConsId, lora_lhs(RHSVars)),
|
|
update_var_origin(CollectPred, LHSVar, OriginLHS, !OriginsMap, !Acc),
|
|
update_var_origins_unify_func_args(CollectPred, Context, LHSVar, ConsId,
|
|
1u, RHSVars, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_unify_func_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, prog_var::in, cons_id::in, uint::in, list(prog_var)::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_unify_func_args(_CollectPred, _Context, _LHSVar, _ConsId,
|
|
_CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_unify_func_args(CollectPred, Context, LHSVar, ConsId,
|
|
CurArgNum, [RHSVar | RHSVars], !OriginsMap, !Acc) :-
|
|
OriginRHS = var_origin_unify_func(Context, ConsId,
|
|
lora_rhs(LHSVar, CurArgNum)),
|
|
update_var_origin(CollectPred, RHSVar, OriginRHS, !OriginsMap, !Acc),
|
|
update_var_origins_unify_func_args(CollectPred, Context, LHSVar, ConsId,
|
|
CurArgNum + 1u, RHSVars, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_plain_call(
|
|
record_var_origin(T)::in(record_var_origin), origin_plain_call::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_plain_call(CollectPred, OriginPlainCall,
|
|
!OriginsMap, !Acc) :-
|
|
OriginPlainCall =
|
|
origin_plain_call(Context, PredId, CalleeSymName, ArgVars),
|
|
update_var_origins_plain_call_args(CollectPred, Context,
|
|
PredId, CalleeSymName, 1u, ArgVars, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_plain_call_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, pred_id::in, sym_name::in, uint::in, list(prog_var)::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_plain_call_args(_CollectPred, _Context,
|
|
_PredId, _CalleeSymName, _CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_plain_call_args(CollectPred, Context, PredId, CalleeSymName,
|
|
CurArgNum, [ArgVar | ArgVars], !OriginsMap, !Acc) :-
|
|
OriginArg =
|
|
var_origin_plain_call(Context, PredId, CalleeSymName, CurArgNum),
|
|
update_var_origin(CollectPred, ArgVar, OriginArg, !OriginsMap, !Acc),
|
|
update_var_origins_plain_call_args(CollectPred, Context,
|
|
PredId, CalleeSymName, CurArgNum + 1u, ArgVars, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_foreign_call(
|
|
record_var_origin(T)::in(record_var_origin), origin_foreign_call::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_foreign_call(CollectPred, OriginPlainCall,
|
|
!OriginsMap, !Acc) :-
|
|
OriginPlainCall =
|
|
origin_foreign_call(Context, PredId, CalleeSymName, ForeignArgs),
|
|
update_var_origins_foreign_call_args(CollectPred, Context,
|
|
PredId, CalleeSymName, 1u, ForeignArgs, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_foreign_call_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, pred_id::in, sym_name::in,
|
|
uint::in, list(foreign_arg)::in, var_origins_map::in, var_origins_map::out,
|
|
T::in, T::out) is det.
|
|
|
|
update_var_origins_foreign_call_args(_CollectPred, _Context,
|
|
_PredId, _CalleeSymName, _CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_foreign_call_args(CollectPred, Context,
|
|
PredId, CalleeSymName, CurArgNum, [ForeignArg | ForeignArgs],
|
|
!OriginsMap, !Acc) :-
|
|
ArgVar = foreign_arg_var(ForeignArg),
|
|
OriginArg =
|
|
var_origin_foreign_call(Context, PredId, CalleeSymName, CurArgNum),
|
|
update_var_origin(CollectPred, ArgVar, OriginArg, !OriginsMap, !Acc),
|
|
update_var_origins_foreign_call_args(CollectPred, Context,
|
|
PredId, CalleeSymName, CurArgNum + 1u, ForeignArgs, !OriginsMap, !Acc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred update_var_origins_generic_call(
|
|
record_var_origin(T)::in(record_var_origin), origin_generic_call::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_generic_call(CollectPred, OriginPlainCall,
|
|
!OriginsMap, !Acc) :-
|
|
OriginPlainCall = origin_generic_call(Context, GCall, ForeignArgs),
|
|
update_var_origins_generic_call_args(CollectPred, Context, GCall,
|
|
1u, ForeignArgs, !OriginsMap, !Acc).
|
|
|
|
:- pred update_var_origins_generic_call_args(
|
|
record_var_origin(T)::in(record_var_origin),
|
|
prog_context::in, generic_call::in, uint::in, list(prog_var)::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origins_generic_call_args(_CollectPred, _Context, _GCall,
|
|
_CurArgNum, [], !OriginsMap, !Acc).
|
|
update_var_origins_generic_call_args(CollectPred, Context, GCall,
|
|
CurArgNum, [ArgVar | ArgVars], !OriginsMap, !Acc) :-
|
|
OriginArg = var_origin_generic_call(Context, GCall, CurArgNum),
|
|
update_var_origin(CollectPred, ArgVar, OriginArg, !OriginsMap, !Acc),
|
|
update_var_origins_generic_call_args(CollectPred, Context, GCall,
|
|
CurArgNum + 1u, ArgVars, !OriginsMap, !Acc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred update_var_origin(record_var_origin(T)::in(record_var_origin),
|
|
prog_var::in, var_origin::in,
|
|
var_origins_map::in, var_origins_map::out, T::in, T::out) is det.
|
|
|
|
update_var_origin(CollectPred, Var, VarOrigin, !OriginsMap, !Acc) :-
|
|
( if map.search(!.OriginsMap, Var, _VarOriginsCord0) then
|
|
true
|
|
else
|
|
VarOriginsCord = set.make_singleton_set(VarOrigin),
|
|
map.det_insert(Var, VarOriginsCord, !OriginsMap),
|
|
CollectPred(!.OriginsMap, Var, VarOrigin, !Acc)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred represent_origins_on_all_branches(list(var_origins_map)::in,
|
|
var_origins_map::out) is det.
|
|
|
|
represent_origins_on_all_branches(OriginsMapList, OriginsMap) :-
|
|
(
|
|
OriginsMapList = [],
|
|
map.init(OriginsMap)
|
|
;
|
|
OriginsMapList = [HeadOriginsMap | TailOriginsMaps],
|
|
map.union_list(set.union, HeadOriginsMap, TailOriginsMaps, OriginsMap)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
explain_var_origin(ModuleInfo, VarTable, Var, Origin, [Msg]) :-
|
|
VarStr = mercury_var_to_string(VarTable, print_name_only, Var),
|
|
(
|
|
Origin = var_origin_clause_head(Context, ClauseNum, ArgNum),
|
|
ArgNumInt = uint.cast_to_int(ArgNum),
|
|
ClauseNumInt = uint.cast_to_int(ClauseNum),
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
nth_fixed(ArgNumInt), words("argument inthe head of"),
|
|
words("clause"), int_fixed(ClauseNumInt), suffix("."), nl]
|
|
;
|
|
Origin = var_origin_lambda_head(Context, _PredOrFunc, ArgNum),
|
|
ArgNumInt = uint.cast_to_int(ArgNum),
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
nth_fixed(ArgNumInt), words("argument of"),
|
|
words("the lambda expression."), nl]
|
|
;
|
|
Origin = var_origin_unify_var(Context, LHSorRHS),
|
|
( LHSorRHS = lor_lhs(OtherVar)
|
|
; LHSorRHS = lor_rhs(OtherVar)
|
|
),
|
|
OtherVarStr =
|
|
mercury_var_to_string(VarTable, print_name_only, OtherVar),
|
|
Pieces = [quote(VarStr), words("is unified with"),
|
|
quote(OtherVarStr), suffix("."), nl]
|
|
;
|
|
Origin = var_origin_unify_func(Context, ConsId, LHSorRHSArg),
|
|
(
|
|
LHSorRHSArg = lora_lhs(RHSArgVars),
|
|
TermStr = functor_cons_id_to_string(ModuleInfo,
|
|
vns_var_table(VarTable), print_name_only, ConsId, RHSArgVars),
|
|
Pieces = [quote(VarStr), words("represents the term"),
|
|
quote(TermStr), suffix("."), nl]
|
|
;
|
|
LHSorRHSArg = lora_rhs(LHSVar, ArgNum),
|
|
ArgNumInt = uint.cast_to_int(ArgNum),
|
|
ConsIdStr = mercury_cons_id_to_string(output_mercury,
|
|
does_not_need_brackets, ConsId),
|
|
LHSVarStr =
|
|
mercury_var_to_string(VarTable, print_name_only, LHSVar),
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
nth_fixed(ArgNumInt), words("argument of the function symbol"),
|
|
quote(ConsIdStr), words("unified with"),
|
|
quote(LHSVarStr), suffix("."), nl]
|
|
)
|
|
;
|
|
(
|
|
Origin = var_origin_plain_call(Context, PredId, CalleeSymName,
|
|
ArgNum)
|
|
;
|
|
Origin = var_origin_foreign_call(Context, PredId, CalleeSymName,
|
|
ArgNum)
|
|
),
|
|
pred_arg_num_description(ModuleInfo, PredId, CalleeSymName, ArgNum,
|
|
MaybePredOrFunc, SymName, ArgNumDescPieces),
|
|
(
|
|
MaybePredOrFunc = yes(PredOrFunc),
|
|
PFPieces = [p_or_f(PredOrFunc)]
|
|
;
|
|
MaybePredOrFunc = no,
|
|
PFPieces = []
|
|
),
|
|
Pieces = [quote(VarStr), words("is the")] ++
|
|
ArgNumDescPieces ++ [words("of the")] ++
|
|
PFPieces ++
|
|
[words("call to"), qual_sym_name(SymName), words("here."), nl]
|
|
;
|
|
Origin = var_origin_generic_call(Context, GenericCall, ArgNum),
|
|
(
|
|
GenericCall = higher_order(_, _, PredOrFunc,
|
|
pred_form_arity(NumArgs), _),
|
|
( if ArgNum = 1u then
|
|
Pieces = [quote(VarStr), words("is the callee of the"),
|
|
words("higher order"), p_or_f(PredOrFunc),
|
|
words("here."), nl]
|
|
else
|
|
ArgNumDescPieces = arg_num_description(PredOrFunc,
|
|
NumArgs - 1, ArgNum),
|
|
Pieces = [quote(VarStr), words("is the")] ++
|
|
ArgNumDescPieces ++ [words("of the"),
|
|
words("higher order"), p_or_f(PredOrFunc),
|
|
words("call here."), nl]
|
|
)
|
|
;
|
|
GenericCall = class_method(_, _, _, PfSNA),
|
|
PfSNA = pf_sym_name_arity(PredOrFunc, SymName,
|
|
pred_form_arity(NumArgs)),
|
|
ArgNumDescPieces =
|
|
arg_num_description(PredOrFunc, NumArgs, ArgNum),
|
|
Pieces = [quote(VarStr), words("is the")] ++
|
|
ArgNumDescPieces ++ [words("of the"),
|
|
p_or_f(PredOrFunc), words("call to method"),
|
|
qual_sym_name(SymName), words("here."), nl]
|
|
;
|
|
GenericCall = event_call(EventName),
|
|
ArgNumInt = uint.cast_to_int(ArgNum),
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
nth_fixed(ArgNumInt), words("argument"),
|
|
words("of event"), quote(EventName), suffix("."), nl]
|
|
;
|
|
GenericCall = cast(CastKind),
|
|
( if ArgNum = 1u then InOrOut = "input" else InOrOut = "output"),
|
|
% I (zs) think that the other CastKinds that should appear here
|
|
% are type coercions.
|
|
(
|
|
( CastKind = unsafe_type_cast
|
|
; CastKind = equiv_type_cast
|
|
),
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
words(InOrOut), words("of a type cast."), nl]
|
|
;
|
|
CastKind = unsafe_type_inst_cast,
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
words(InOrOut), words("of a type and inst cast."), nl]
|
|
;
|
|
CastKind = exists_cast,
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
words(InOrOut), words("of an existential type cast."), nl]
|
|
;
|
|
CastKind = subtype_coerce,
|
|
Pieces = [quote(VarStr), words("is the"),
|
|
words(InOrOut), words("of a type coercion."), nl]
|
|
)
|
|
)
|
|
),
|
|
Msg = msg(Context, Pieces).
|
|
|
|
:- pred pred_arg_num_description(module_info::in, pred_id::in, sym_name::in,
|
|
uint::in, maybe(pred_or_func)::out, sym_name::out,
|
|
list(format_piece)::out) is det.
|
|
|
|
pred_arg_num_description(ModuleInfo, PredId, CalleeSymName, ArgNum,
|
|
MaybePredOrFunc, SymName, ArgNumPieces) :-
|
|
module_info_get_pred_id_table(ModuleInfo, PredIdTable),
|
|
( if map.search(PredIdTable, PredId, PredInfo) then
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
MaybePredOrFunc = yes(PredOrFunc),
|
|
pred_info_get_sym_name(PredInfo, SymName),
|
|
pred_form_arity(NumArgs) = pred_info_pred_form_arity(PredInfo),
|
|
ArgNumPieces = arg_num_description(PredOrFunc, NumArgs, ArgNum)
|
|
else
|
|
% We get here when the source of an unresolved ambiguity
|
|
% in the type of a variable is caused by an unresolved ambiguity
|
|
% about which of several predicates (or functions) a particular
|
|
% call actually refers to. This occurred in an example program
|
|
% posted by Volver Wysk to m-rev on 2025 oct 19 (Melbourne time).
|
|
MaybePredOrFunc = no,
|
|
SymName = CalleeSymName,
|
|
ArgNumPieces = [unth_fixed(ArgNum), words("argument")]
|
|
).
|
|
|
|
:- func arg_num_description(pred_or_func, int, uint) = list(format_piece).
|
|
|
|
arg_num_description(PredOrFunc, NumArgs, ArgNum) = Pieces :-
|
|
ArgNumInt = uint.cast_to_int(ArgNum),
|
|
( if
|
|
PredOrFunc = pf_function,
|
|
ArgNumInt = NumArgs
|
|
then
|
|
Pieces = [words("function result")]
|
|
else
|
|
Pieces = [nth_fixed(ArgNumInt), words("argument")]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.var_origins.
|
|
%---------------------------------------------------------------------------%
|