Files
mercury/compiler/assertion.m
Zoltan Somogyi c34f6dd4e8 Let the compiler bootstrap in decldebug grades again.
The big diff that replaced the item list with proper parse trees included code
that the compiler couldn't handle in decldebug grades, aborting with a message
about liveness mismatch between different switch arms. The problem occurred
when compiling this code in generate_missing_start_section_warning_int,
which is one arm of a switch on !.MissingStartSectionWarning:

    !.MissingStartSectionWarning =
        have_not_given_missing_section_start_warning,
    !:MissingStartSectionWarning =
        have_given_missing_section_start_warning,
    Pieces = [invis_order_default_start(1), ...],
    _Spec = error_spec(severity_error, phase_term_to_parse_tree,
        [simple_msg(Context, [always(Pieces)])])

The problem was liveness.m's handling of the from_ground_term_construct
scope that is generated for the assigmnet of the ground term to Pieces.

compiler/liveness.m:
    During the first pass through the procedure body, the liveness pass,
    replace with the empty conjunction any from_ground_term_construct scopes
    that construct terms to assign to dead variables. This prevents the
    second pass, the deadness pass, from including the dead variable
    (Pieces in the above example, after the assignment to _Spec was
    optimized away by earlier passes) in the set of variables that
    have been seen, since obviously, they won't be seen in the other arms.
    This inclusion was the cause of the assertion failure that led to
    the compiler abort.

    Improve the existing debugging infrastructure to help find bugs like this,
    by printing out what the selected procedure's body looks like before
    as well as after the initial requantification.

compiler/quantification.m:
    Replace from_ground_term_construct scopes whose ground term is assigned
    to a dead variable with empty conjunctions, so that the compiler passes
    between the first quantification after mode checking and the liveness
    pass also see a smaller HLDS.

    Implement an unrelated improvement whose possibility I discovered when
    investigating why a version of the test case *without* the from_ground_term
    scope wasn't getting the abort. When processing unifications, build up the
    set of variables in the unification directly, not by recording a bunch
    of maybe(somethings)s and then processing the somethings, if they existed,
    later. This yields code that is shorter, simpler *and* faster.

tests/valid/liveness_disagree.m:
    A new test case for this bug. It is a cut-down version of
    generate_missing_start_section_warning_int.

tests/valid/Mmakefile:
tests/valid/Mercury.options:
    Enable the new test case, and try to compile it in a declarative debugging
    grade.

compiler/assertion.m:
compiler/deep_profiling.m:
compiler/lookup_switch.m:
compiler/par_loop_control.m:
    Add module qualifications in several places. I found the need for these
    when I temporarily redefined the set_of_var type to use sets instead of
    sparse_bitsets, to make debugging the bug in liveness.m easier; without
    them, I got errors about excessive overloading.

    Do some other cleanups as well.

tests/debugger/all_solutions.exp4:
tests/debugger/exception_cmd.exp3:
tests/debugger/loopcheck.exp3:
tests/debugger/uci_index.exp2:
tests/declarative_debugger/Mmakefile:
tests/declarative_debugger/builtin_call_rep.exp:
tests/declarative_debugger/catch_retry.exp:
tests/declarative_debugger/condition_bug.exp:
tests/declarative_debugger/find_origin.exp3:
tests/declarative_debugger/lpe_example.exp3:
tests/declarative_debugger/priv_builtin_bug.exp:
tests/declarative_debugger/solns.exp3:
tests/declarative_debugger/sort.exp:
tests/declarative_debugger/track_through_catch.exp:
tests/declarative_debugger/typed_unify.exp:
    Update all these expected outputs for the changes in line numbers
    caused by my cleanups of the test cases early in 2015.

    For the declarative_debugger/{condition_bug,sort} test cases, also
    update them for the change to print the types and values of foreign types.

tests/hard_coded/type_qual.{m,exp}:
    This test case failed for an earlier version of this diff, so add some
    context to its outputs, to make such failures easier to debug.
    To make *that* easier, bring it to date in programming style.
2015-08-29 13:07:01 +10:00

784 lines
29 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% Module: assertion.m.
% Main authors: petdr.
%
% This module is an abstract interface to the assertion table.
% Note that this is a first design and will probably change
% substantially in the future.
%
%-----------------------------------------------------------------------------%
:- module hlds.assertion.
:- interface.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module pair.
%-----------------------------------------------------------------------------%
% Get the hlds_goal which represents the assertion.
%
:- pred assert_id_goal(module_info::in, assert_id::in, hlds_goal::out) is det.
% Record into the pred_info of each pred used in the assertion
% the assert_id.
%
:- pred record_preds_used_in(hlds_goal::in, assert_id::in,
module_info::in, module_info::out) is det.
% is_commutativity_assertion(MI, Id, Vs, CVs):
%
% Does the assertion represented by the assertion id, Id,
% state the commutativity of a pred/func?
% We extend the usual definition of commutativity to apply to
% predicates or functions with more than two arguments as
% follows by allowing extra arguments which must be invariant.
% If so, this predicate returns (in CVs) the two variables which
% can be swapped in order if it was a call to Vs.
%
% The assertion must be in a form similar to this
% all [Is,A,B,C] ( p(Is,A,B,C) <=> p(Is,B,A,C) )
% for the predicate to return true (note that the invariant
% arguments, Is, can be any where providing they are in
% identical locations on both sides of the equivalence).
%
:- pred is_commutativity_assertion(module_info::in, assert_id::in,
list(prog_var)::in, pair(prog_var)::out) is semidet.
% is_associativity_assertion(MI, Id, Vs, CVs, OV):
%
% Does the assertion represented by the assertion id, Id,
% state the associativity of a pred/func?
% We extend the usual definition of associativity to apply to
% predicates or functions with more than two arguments as
% follows by allowing extra arguments which must be invariant.
% If so, this predicate returns (in CVs) the two variables which
% can be swapped in order if it was a call to Vs, and the
% output variable, OV, related to these two variables (for the
% case below it would be the variable in the same position as
% AB, BC or ABC).
%
% The assertion must be in a form similar to this
%
% all [Is,A,B,C,ABC]
% (
% some [AB] p(Is,A,B,AB), p(Is,AB,C,ABC)
% <=>
% some [BC] p(Is,B,C,BC), p(Is,A,BC,ABC)
% )
%
% for the predicate to return true (note that the invariant
% arguments, Is, can be any where providing they are in
% identical locations on both sides of the equivalence).
%
:- pred is_associativity_assertion(module_info::in, assert_id::in,
list(prog_var)::in, pair(prog_var)::out, prog_var::out) is semidet.
% is_update_assertion(MI, Id, PId, Ss):
%
% is true iff the assertion, Id, is about a predicate, PId,
% which takes some state as input and produces some state as output
% and we are guaranteed to get the same final state regardless of
% the order that the state is updated.
%
% i.e. the promise should look something like this, note that A
% and B could be vectors of variables.
%
% :- promise all [A,B,SO,S]
% (
% (some [SA] (update(S0,A,SA), update(SA,B,S)))
% <=>
% (some [SB] (update(S0,B,SB), update(SB,A,S)))
% ).
%
% Given the actual variables, Vs, to the call to update, return
% the pair of variables which are state variables, SPair.
%
:- pred is_update_assertion(module_info::in, assert_id::in,
pred_id::in, list(prog_var)::in, pair(prog_var)::out) is semidet.
% is_construction_equivalence_assertion(MI, Id, C, P):
%
% Can a single construction unification whose functor is determined
% by the cons_id, C, be expressed as a call to the predid, P (with possibly
% some construction unifications to initialise the arguments).
%
% The assertion will be in a form similar to
%
% all [L,H,T] ( L = [H | T] <=> append([H], T, L) )
%
:- pred is_construction_equivalence_assertion(module_info::in, assert_id::in,
cons_id::in, pred_id::in) is semidet.
% Place a hlds_goal into a standard form. Currently all the code does
% is replace conj([G]) with G.
%
:- pred normalise_goal(hlds_goal::in, hlds_goal::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_clauses.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.set_of_var.
:- import_module assoc_list.
:- import_module map.
:- import_module maybe.
:- import_module require.
:- import_module set.
:- import_module solutions.
:- type subst == map(prog_var, prog_var).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
is_commutativity_assertion(Module, AssertId, CallVars, CommutativeVars) :-
assert_id_goal(Module, AssertId, Goal),
goal_is_equivalence(Goal, P, Q),
P = hlds_goal(plain_call(PredId, _, VarsP, _, _, _), _),
Q = hlds_goal(plain_call(PredId, _, VarsQ, _, _, _), _),
commutative_var_ordering(VarsP, VarsQ, CallVars, CommutativeVars).
% commutative_var_ordering(Ps, Qs, Vs, CommutativeVs):
%
% Check that the two list of variables are identical except that
% the position of two variables has been swapped.
% For example, commutative_var_ordering is true for [A,B,C] and [B,A,C].
% It also takes a list of variables, Vs, to a call and returns
% the two variables in that list that can be swapped. In this case,
% that will be [A,B].
%
:- pred commutative_var_ordering(list(prog_var)::in, list(prog_var)::in,
list(prog_var)::in, pair(prog_var)::out) is semidet.
commutative_var_ordering([P | Ps], [Q | Qs], [V | Vs], CommutativeVars) :-
( if P = Q then
commutative_var_ordering(Ps, Qs, Vs, CommutativeVars)
else
commutative_var_ordering_2(P, Q, Ps, Qs, Vs, CallVarB),
CommutativeVars = V - CallVarB
).
:- pred commutative_var_ordering_2(prog_var::in, prog_var::in,
list(prog_var)::in, list(prog_var)::in, list(prog_var)::in,
prog_var::out) is semidet.
commutative_var_ordering_2(VarP, VarQ, [P | Ps], [Q | Qs], [V | Vs],
CallVarB) :-
( if P = Q then
commutative_var_ordering_2(VarP, VarQ, Ps, Qs, Vs, CallVarB)
else
CallVarB = V,
P = VarQ,
Q = VarP,
Ps = Qs
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
is_associativity_assertion(Module, AssertId, CallVars,
AssociativeVars, OutputVar) :-
assert_id_goal(Module, AssertId, hlds_goal(GoalExpr, GoalInfo)),
goal_is_equivalence(hlds_goal(GoalExpr, GoalInfo), P, Q),
UniversiallyQuantifiedVars = goal_info_get_nonlocals(GoalInfo),
get_conj_goals(P, PCalls),
get_conj_goals(Q, QCalls),
promise_equivalent_solutions [AssociativeVars, OutputVar] (
associative(PCalls, QCalls, UniversiallyQuantifiedVars, CallVars,
AssociativeVars - OutputVar)
).
% associative(Ps, Qs, Us, R):
%
% If the assertion was in the form
% all [Us] (some [] (Ps)) <=> (some [] (Qs))
% try and rearrange the order of Ps and Qs so that the assertion
% is in the standard from
%
% compose( A, B, AB), compose(B, C, BC),
% compose(AB, C, ABC) <=> compose(A, BC, ABC)
%
:- pred associative(hlds_goals::in, hlds_goals::in,
set_of_progvar::in, list(prog_var)::in,
pair(pair(prog_var), prog_var)::out) is cc_nondet.
associative(PCalls, QCalls, UniversiallyQuantifiedVars, CallVars,
(CallVarA - CallVarB) - OutputVar) :-
reorder(PCalls, QCalls, LHSCalls, RHSCalls),
process_one_side(LHSCalls, UniversiallyQuantifiedVars, PredId,
AB, PairsL, Vs),
process_one_side(RHSCalls, UniversiallyQuantifiedVars, PredId,
BC, PairsR, _),
% If you read the predicate documentation, you will note that
% for each pair of variables on the left hand side there are an equivalent
% pair of variables on the right hand side. As the pairs of variables
% are not symmetric, the call to list.perm will only succeed once,
% if at all.
assoc_list.from_corresponding_lists(PairsL, PairsR, Pairs),
list.perm(Pairs, [(A - AB) - (B - A), (B - C) - (C - BC),
(AB - ABC) - (BC - ABC)]),
assoc_list.from_corresponding_lists(Vs, CallVars, AssocList),
list.filter((pred(X - _Y::in) is semidet :- X = AB),
AssocList, [_AB - OutputVar]),
list.filter((pred(X - _Y::in) is semidet :- X = A),
AssocList, [_A - CallVarA]),
list.filter((pred(X - _Y::in) is semidet :- X = B),
AssocList, [_B - CallVarB]).
% reorder(Ps, Qs, Ls, Rs):
%
% Given both sides of the equivalence return another possible ordering.
%
:- pred reorder(hlds_goals::in, hlds_goals::in,
hlds_goals::out, hlds_goals::out) is multi.
reorder(PCalls, QCalls, LHSCalls, RHSCalls) :-
list.perm(PCalls, LHSCalls),
list.perm(QCalls, RHSCalls).
reorder(PCalls, QCalls, LHSCalls, RHSCalls) :-
list.perm(PCalls, RHSCalls),
list.perm(QCalls, LHSCalls).
% process_one_side(Gs, Us, L, Ps):
%
% Given the list of goals, Gs, which are one side of a possible
% associative equivalence, and the universally quantified
% variables, Us, of the goals return L the existentially
% quantified variable that links the two calls and Ps the list
% of variables which are not invariants.
%
% i.e. for app(TypeInfo, X, Y, XY), app(TypeInfo, XY, Z, XYZ)
% L <= XY and Ps <= [X - XY, Y - Z, XY - XYZ]
%
:- pred process_one_side(hlds_goals::in, set_of_progvar::in, pred_id::out,
prog_var::out, assoc_list(prog_var)::out, list(prog_var)::out) is semidet.
process_one_side(Goals, UniversiallyQuantifiedVars, PredId,
LinkingVar, Vars, VarsA) :-
process_two_linked_calls(Goals, UniversiallyQuantifiedVars, PredId,
LinkingVar, Vars0, VarsA),
% Filter out all the invariant arguments, and then make sure that
% there are only 3 arguments left.
list.filter((pred(X - Y::in) is semidet :- X \= Y), Vars0, Vars),
list.length(Vars, number_of_associative_vars).
:- func number_of_associative_vars = int.
number_of_associative_vars = 3.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
is_update_assertion(Module, AssertId, _PredId, CallVars, StateA - StateB) :-
assert_id_goal(Module, AssertId, hlds_goal(GoalExpr, GoalInfo)),
goal_is_equivalence(hlds_goal(GoalExpr, GoalInfo), P, Q),
UniversiallyQuantifiedVars = goal_info_get_nonlocals(GoalInfo),
get_conj_goals(P, PCalls),
get_conj_goals(Q, QCalls),
solutions.solutions(
update(PCalls, QCalls, UniversiallyQuantifiedVars, CallVars),
[StateA - StateB | _]).
% compose(S0, A, SA), compose(SB, A, S),
% compose(SA, B, S) <=> compose(S0, B, SB)
%
:- pred update(hlds_goals::in, hlds_goals::in, set_of_progvar::in,
list(prog_var)::in, pair(prog_var)::out) is nondet.
update(PCalls, QCalls, UniversiallyQuantifiedVars, CallVars,
StateA - StateB) :-
reorder(PCalls, QCalls, LHSCalls, RHSCalls),
process_two_linked_calls(LHSCalls, UniversiallyQuantifiedVars, PredId,
SA, PairsL, Vs),
process_two_linked_calls(RHSCalls, UniversiallyQuantifiedVars, PredId,
SB, PairsR, _),
assoc_list.from_corresponding_lists(PairsL, PairsR, Pairs0),
list.filter((pred(X - Y::in) is semidet :- X \= Y), Pairs0, Pairs),
list.length(Pairs) = 2,
% If you read the predicate documentation, you will note that
% for each pair of variables on the left hand side there is an equivalent
% pair of variables on the right hand side. As the pairs of variables
% are not symmetric, the call to list.perm will only succeed once,
% if at all.
list.perm(Pairs, [(S0 - SA) - (SB - S0), (SA - S) - (S - SB)]),
assoc_list.from_corresponding_lists(Vs, CallVars, AssocList),
list.filter((pred(X - _Y::in) is semidet :- X = S0),
AssocList, [_S0 - StateA]),
list.filter((pred(X - _Y::in) is semidet :- X = SA),
AssocList, [_SA - StateB]).
%-----------------------------------------------------------------------------%
% process_two_linked_calls(Gs, UQVs, PId, LV, AL, VAs):
%
% is true iff the list of goals, Gs, with universally quantified
% variables, UQVs, is two calls to the same predicate, PId, with
% one variable that links them, LV. AL will be the assoc list
% that is the each variable from the first call with its
% corresponding variable in the second call, and VAs are the
% variables of the first call.
%
:- pred process_two_linked_calls(hlds_goals::in, set_of_progvar::in,
pred_id::out, prog_var::out, assoc_list(prog_var)::out,
list(prog_var)::out) is semidet.
process_two_linked_calls(Goals, UniversiallyQuantifiedVars, PredId,
LinkingVar, Vars, VarsA) :-
Goals = [
hlds_goal(plain_call(PredId, _, VarsA, _, _, _), _),
hlds_goal(plain_call(PredId, _, VarsB, _, _, _), _)
],
% Determine the linking variable, L. By definition it must be
% existentially quantified and a member of both variable lists.
SetVarsA = set_of_var.list_to_set(VarsA),
SetVarsB = set_of_var.list_to_set(VarsB),
CommonVars = set_of_var.intersect(SetVarsA, SetVarsB),
set_of_var.is_singleton(
set_of_var.difference(CommonVars, UniversiallyQuantifiedVars),
LinkingVar),
% Set up mapping between the variables in the two calls.
assoc_list.from_corresponding_lists(VarsA, VarsB, Vars).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
is_construction_equivalence_assertion(Module, AssertId, ConsId, PredId) :-
assert_id_goal(Module, AssertId, Goal),
goal_is_equivalence(Goal, P, Q),
( if single_construction(P, ConsId) then
predicate_call(Q, PredId)
else
single_construction(Q, ConsId),
predicate_call(P, PredId)
).
% One side of the equivalence must be just the single unification
% with the correct cons_id.
%
:- pred single_construction(hlds_goal::in, cons_id::in) is semidet.
single_construction(Goal, ConsId) :-
Goal = hlds_goal(GoalExpr, _),
GoalExpr = unify(_, UnifyRHS, _, _, _),
UnifyRHS = rhs_functor(cons(UnqualifiedSymName, Arity, _TypeCtorA), _, _),
ConsId = cons(QualifiedSymName, Arity, _TypeCtorB),
% Before post-typecheck, TypeCtorA and TypeCtorB would be dummies,
% and would thus match even if the two functors are NOT of the same type.
% Note that by insisting on cons, we effectively disallow assertions
% about tuples.
partial_sym_name_matches_full(UnqualifiedSymName, QualifiedSymName).
% The side containing the predicate call must be a single call
% to the predicate with 0 or more construction unifications
% which setup the arguments to the predicates.
%
:- pred predicate_call(hlds_goal::in, pred_id::in) is semidet.
predicate_call(Goal, PredId) :-
( if Goal = hlds_goal(conj(plain_conj, Goals), _) then
list.member(Call, Goals),
Call = hlds_goal(plain_call(PredId, _, _, _, _, _), _),
list.delete(Goals, Call, Unifications),
P = (pred(G::in) is semidet :-
not (
G = hlds_goal(unify(_, UnifyRhs, _, _, _), _),
UnifyRhs = rhs_functor(_, _, _)
)
),
list.filter(P, Unifications, [])
else
Goal = hlds_goal(plain_call(PredId, _, _, _, _, _), _)
).
%-----------------------------------------------------------------------------%
:- pred get_conj_goals(hlds_goal::in, list(hlds_goal)::out) is semidet.
get_conj_goals(Goal0, ConjList) :-
% The user may have explicitly quantified the goals.
ignore_exist_quant_scope(Goal0, Goal),
Goal = hlds_goal(conj(plain_conj, ConjList), _).
:- pred ignore_exist_quant_scope(hlds_goal::in, hlds_goal::out) is det.
ignore_exist_quant_scope(Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr0, _Context),
( if
GoalExpr0 = scope(Reason, Goal1),
Reason = exist_quant(_)
then
Goal = Goal1
else
Goal = Goal0
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assert_id_goal(Module, AssertId, Goal) :-
module_info_get_assertion_table(Module, AssertTable),
assertion_table_lookup(AssertTable, AssertId, PredId),
module_info_pred_info(Module, PredId, PredInfo),
pred_info_get_clauses_info(PredInfo, ClausesInfo),
clauses_info_get_clauses_rep(ClausesInfo, ClausesRep, _ItemNumbers),
get_clause_list_maybe_repeated(ClausesRep, Clauses),
(
Clauses = [Clause],
Goal0 = Clause ^ clause_body,
normalise_goal(Goal0, Goal)
;
( Clauses = []
; Clauses = [_, _ | _]
),
unexpected($module, $pred, "goal is not an assertion")
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred goal_is_implication(hlds_goal::in, hlds_goal::out, hlds_goal::out)
is semidet.
goal_is_implication(Goal, P, Q) :-
% Goal = (P => Q)
Goal = hlds_goal(negation(hlds_goal(conj(plain_conj, GoalList), _)), GI),
list.reverse(GoalList) = [NotQ | Ps],
( if Ps = [P0] then
P = P0
else
P = hlds_goal(conj(plain_conj, list.reverse(Ps)), GI)
),
NotQ = hlds_goal(negation(Q), _).
:- pred goal_is_equivalence(hlds_goal::in, hlds_goal::out, hlds_goal::out)
is semidet.
goal_is_equivalence(Goal, P, Q) :-
% Goal = P <=> Q
Goal = hlds_goal(conj(plain_conj, [A, B]), _GoalInfo),
map.init(Subst),
goal_is_implication(A, PA, QA),
goal_is_implication(B, QB, PB),
equal_goals(PA, PB, Subst, _),
equal_goals(QA, QB, Subst, _),
P = PA,
Q = QA.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% equal_goals(GA, GB):
%
% Do these two goals represent the same hlds_goal modulo renaming?
%
:- pred equal_goals(hlds_goal::in, hlds_goal::in,
subst::in, subst::out) is semidet.
equal_goals(GoalA, GoalB, !Subst) :-
GoalA = hlds_goal(GoalExprA, _GoalInfoA),
GoalB = hlds_goal(GoalExprB, _GoalInfoB),
equal_goal_exprs(GoalExprA, GoalExprB, !Subst).
:- pred equal_goal_exprs(hlds_goal_expr::in, hlds_goal_expr::in,
subst::in, subst::out) is semidet.
equal_goal_exprs(GoalExprA, GoalExprB, !Subst) :-
(
GoalExprA = conj(ConjType, GoalsA),
GoalExprB = conj(ConjType, GoalsB),
equal_goals_list(GoalsA, GoalsB, !Subst)
;
GoalExprA = plain_call(PredId, _, ArgVarsA, _, _, _),
GoalExprB = plain_call(PredId, _, ArgVarsB, _, _, _),
equal_vars(ArgVarsA, ArgVarsB, !Subst)
;
GoalExprA = generic_call(CallDetails, ArgVarsA, _, _, _),
GoalExprB = generic_call(CallDetails, ArgVarsB, _, _, _),
equal_vars(ArgVarsA, ArgVarsB, !Subst)
;
GoalExprA = switch(Var, CanFail, CasesA),
GoalExprB = switch(Var, CanFail, CasesB),
equal_goals_cases(CasesA, CasesB, !Subst)
;
GoalExprA = unify(VarA, RHSA, _, _, _),
GoalExprB = unify(VarB, RHSB, _, _, _),
equal_var(VarA, VarB, !Subst),
equal_unification(RHSA, RHSB, !Subst)
;
GoalExprA = disj(GoalsA),
GoalExprB = disj(GoalsB),
equal_goals_list(GoalsA, GoalsB, !Subst)
;
GoalExprA = negation(SubGoalA),
GoalExprB = negation(SubGoalB),
equal_goals(SubGoalA, SubGoalB, !Subst)
;
GoalExprA = scope(ReasonA, SubGoalA),
GoalExprB = scope(ReasonB, SubGoalB),
equal_reason(ReasonA, ReasonB, !Subst),
equal_goals(SubGoalA, SubGoalB, !Subst)
;
GoalExprA = if_then_else(VarsA, CondA, ThenA, ElseA),
GoalExprB = if_then_else(VarsB, CondB, ThenB, ElseB),
equal_vars(VarsA, VarsB, !Subst),
equal_goals(CondA, CondB, !Subst),
equal_goals(ThenA, ThenB, !Subst),
equal_goals(ElseA, ElseB, !Subst)
;
GoalExprA = call_foreign_proc(Attributes, PredId, _,
ArgsA, ExtraA, MaybeTraceA, _),
GoalExprB = call_foreign_proc(Attributes, PredId, _,
ArgsB, ExtraB, MaybeTraceB, _),
% Foreign_procs with extra args and trace runtime conditions are
% compiler generated, and as such will not participate in assertions.
ExtraA = [],
ExtraB = [],
MaybeTraceA = no,
MaybeTraceB = no,
VarsA = list.map(foreign_arg_var, ArgsA),
VarsB = list.map(foreign_arg_var, ArgsB),
equal_vars(VarsA, VarsB, !Subst)
;
GoalExprA = shorthand(ShortHandA),
GoalExprB = shorthand(ShortHandB),
equal_goals_shorthand(ShortHandA, ShortHandB, !Subst)
).
:- pred equal_reason(scope_reason::in, scope_reason::in, subst::in, subst::out)
is semidet.
equal_reason(exist_quant(VarsA), exist_quant(VarsB), !Subst) :-
equal_vars(VarsA, VarsB, !Subst).
equal_reason(barrier(Removable), barrier(Removable), !Subst).
equal_reason(commit(ForcePruning), commit(ForcePruning), !Subst).
equal_reason(from_ground_term(VarA, Kind), from_ground_term(VarB, Kind),
!Subst) :-
equal_var(VarA, VarB, !Subst).
:- pred equal_goals_shorthand(shorthand_goal_expr::in, shorthand_goal_expr::in,
subst::in, subst::out) is semidet.
equal_goals_shorthand(ShortHandA, ShortHandB, !Subst) :-
ShortHandA = bi_implication(LeftGoalA, RightGoalA),
ShortHandB = bi_implication(LeftGoalB, RightGoalB),
equal_goals(LeftGoalA, LeftGoalB, !Subst),
equal_goals(RightGoalA, RightGoalB, !Subst).
:- pred equal_var(prog_var::in, prog_var::in, subst::in, subst::out)
is semidet.
equal_var(VA, VB, !Subst) :-
( if map.search(!.Subst, VA, SubstVA) then
SubstVA = VB
else
map.insert(VA, VB, !Subst)
).
:- pred equal_vars(list(prog_var)::in, list(prog_var)::in,
subst::in, subst::out) is semidet.
equal_vars([], [], !Subst).
equal_vars([VA | VAs], [VB | VBs], !Subst) :-
equal_var(VA, VB, !Subst),
equal_vars(VAs, VBs, !Subst).
:- pred equal_unification(unify_rhs::in, unify_rhs::in, subst::in, subst::out)
is semidet.
equal_unification(rhs_var(A), rhs_var(B), !Subst) :-
equal_vars([A], [B], !Subst).
equal_unification(rhs_functor(ConsId, E, VarsA), rhs_functor(ConsId, E, VarsB),
!Subst) :-
equal_vars(VarsA, VarsB, !Subst).
equal_unification(LambdaGoalA, LambdaGoalB, !Subst) :-
LambdaGoalA = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
NLVarsA, LVarsA, Modes, Det, GoalA),
LambdaGoalB = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
NLVarsB, LVarsB, Modes, Det, GoalB),
equal_vars(NLVarsA, NLVarsB, !Subst),
equal_vars(LVarsA, LVarsB, !Subst),
equal_goals(GoalA, GoalB, !Subst).
:- pred equal_goals_list(hlds_goals::in, hlds_goals::in, subst::in, subst::out)
is semidet.
equal_goals_list([], [], !Subst).
equal_goals_list([GoalA | GoalAs], [GoalB | GoalBs], !Subst) :-
equal_goals(GoalA, GoalB, !Subst),
equal_goals_list(GoalAs, GoalBs, !Subst).
:- pred equal_goals_cases(list(case)::in, list(case)::in,
subst::in, subst::out) is semidet.
equal_goals_cases([], [], !Subst).
equal_goals_cases([CaseA | CaseAs], [CaseB | CaseBs], !Subst) :-
CaseA = case(MainConsIdA, OtherConsIdsA, GoalA),
CaseB = case(MainConsIdB, OtherConsIdsB, GoalB),
list.sort([MainConsIdA | OtherConsIdsA], SortedConsIds),
list.sort([MainConsIdB | OtherConsIdsB], SortedConsIds),
equal_goals(GoalA, GoalB, !Subst),
equal_goals_cases(CaseAs, CaseBs, !Subst).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
record_preds_used_in(Goal, AssertId, !Module) :-
predids_from_goal(Goal, PredIds),
% Sanity check.
( if list.member(invalid_pred_id, PredIds) then
unexpected($module, $pred, "invalid pred_id")
else
true
),
list.foldl(update_pred_info(AssertId), PredIds, !Module).
%-----------------------------------------------------------------------------%
% update_pred_info(Id, A, !Module):
%
% Record in the pred_info pointed to by Id that that predicate
% is used in the assertion pointed to by A.
%
:- pred update_pred_info(assert_id::in, pred_id::in,
module_info::in, module_info::out) is det.
update_pred_info(AssertId, PredId, !Module) :-
module_info_pred_info(!.Module, PredId, PredInfo0),
pred_info_get_assertions(PredInfo0, Assertions0),
set.insert(AssertId, Assertions0, Assertions),
pred_info_set_assertions(Assertions, PredInfo0, PredInfo),
module_info_set_pred_info(PredId, PredInfo, !Module).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
normalise_goal(Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo),
normalise_goal_expr(GoalExpr0, GoalExpr),
Goal = hlds_goal(GoalExpr, GoalInfo).
:- pred normalise_goal_expr(hlds_goal_expr::in, hlds_goal_expr::out) is det.
normalise_goal_expr(GoalExpr0, GoalExpr) :-
(
( GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalExpr = GoalExpr0
;
GoalExpr0 = conj(ConjType, Goals0),
(
ConjType = plain_conj,
normalise_conj(Goals0, Goals)
;
ConjType = parallel_conj,
normalise_goals(Goals0, Goals)
),
GoalExpr = conj(ConjType, Goals)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
normalise_cases(Cases0, Cases),
GoalExpr = switch(Var, CanFail, Cases)
;
GoalExpr0 = disj(Goals0),
normalise_goals(Goals0, Goals),
GoalExpr = disj(Goals)
;
GoalExpr0 = negation(SubGoal0),
normalise_goal(SubGoal0, SubGoal),
GoalExpr = negation(SubGoal)
;
GoalExpr0 = scope(Reason, SubGoal0),
normalise_goal(SubGoal0, SubGoal),
GoalExpr = scope(Reason, SubGoal)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
normalise_goal(Cond0, Cond),
normalise_goal(Then0, Then),
normalise_goal(Else0, Else),
GoalExpr = if_then_else(Vars, Cond, Then, Else)
;
GoalExpr0 = shorthand(ShortHand0),
(
ShortHand0 = atomic_goal(GoalType, Outer, Inner, Vars,
MainGoal0, OrElseAlternatives0, OrElseInners),
normalise_goal(MainGoal0, MainGoal),
normalise_goals(OrElseAlternatives0, OrElseAlternatives),
ShortHand = atomic_goal(GoalType, Outer, Inner, Vars, MainGoal,
OrElseAlternatives, OrElseInners)
;
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
normalise_goal(SubGoal0, SubGoal),
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
;
ShortHand0 = bi_implication(LHS0, RHS0),
normalise_goal(LHS0, LHS),
normalise_goal(RHS0, RHS),
ShortHand = bi_implication(LHS, RHS)
),
GoalExpr = shorthand(ShortHand)
).
%-----------------------------------------------------------------------------%
:- pred normalise_conj(hlds_goals::in, hlds_goals::out) is det.
normalise_conj([], []).
normalise_conj([Goal0 | Goals0], Goals) :-
goal_to_conj_list(Goal0, ConjGoals),
normalise_conj(Goals0, Goals1),
list.append(ConjGoals, Goals1, Goals).
:- pred normalise_cases(list(case)::in, list(case)::out) is det.
normalise_cases([], []).
normalise_cases([Case0 | Cases0], [Case | Cases]) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
normalise_goal(Goal0, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
normalise_cases(Cases0, Cases).
:- pred normalise_goals(hlds_goals::in, hlds_goals::out) is det.
normalise_goals([], []).
normalise_goals([Goal0 | Goals0], [Goal | Goals]) :-
normalise_goal(Goal0, Goal),
normalise_goals(Goals0, Goals).
%-----------------------------------------------------------------------------%
:- end_module hlds.assertion.
%-----------------------------------------------------------------------------%