Files
mercury/compiler/assertion.m
Peter Ross 05042c60ca Rewrite the accumulator introduction algorithm, so that it is a
Estimated hours taken: 70

Rewrite the accumulator introduction algorithm, so that it is a
combination of the algorithms presented in "Making Mercury Programs Tail
Recursive" and "State Update Transformation".

compiler/accumulator.m:
    Rewrite and fix two bugs.
    The bugs fixed were
        * construction unifications which depend on associative calls
          are handled correctly (not a problem in 0.9.x as the only
          associative call it handled was list__append).
        * that commutativity implied associativity, now check to see
          whether an associative predicate has the addition property of
          commutativity.

compiler/assertion.m:
    assertion__is_associativity_assertion now reports the output
    variable which is constructed from the two associative input
    variables.  It can now also handle the case where there is no
    explicit existential quantification.
    Add assertion__is_construction_equivalence_assertion which
    recognises when a predicate is equivalent with a constrution
    unification.
    Add assertion__is_update_assertion which recognises when a predicate
    can update some state in an arbitary order.

compiler/goal_store.m:
    A module to associate arbitrary Ids with a hlds_goal and the instmap
    before the goal.

library/int.m:
    Add associativity declarations for + and *.

library/list.m:
    Add the law
    :- promise all [L,H,T] ( L = [H|T] <=> list__append([H], T, L)).
2000-04-12 09:48:24 +00:00

896 lines
32 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2000 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
%
% 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 (assertion).
:- interface.
:- import_module hlds_data, hlds_goal, hlds_module, hlds_pred, prog_data.
:- import_module io, std_util.
%
% assertion__goal
%
% Get the hlds_goal which represents the assertion.
%
:- pred assertion__goal(assert_id::in, module_info::in, hlds_goal::out) is det.
%
% assertion__record_preds_used_in
%
% Record into the pred_info of each pred used in the assertion
% the assert_id.
%
:- pred assertion__record_preds_used_in(hlds_goal::in, assert_id::in,
module_info::in, module_info::out) is det.
%
% assertion__is_commutativity_assertion(Id, MI, 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 assertion__is_commutativity_assertion(assert_id::in, module_info::in,
prog_vars::in, pair(prog_var)::out) is semidet.
%
%
% assertion__is_associativity_assertion(Id, MI, 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 assertion__is_associativity_assertion(assert_id::in, module_info::in,
prog_vars::in, pair(prog_var)::out, prog_var::out) is semidet.
%
% assertion__is_associativity_assertion(Id, MI, PId, Vs, SPair)
%
% Recognise assertions in the form
% all [A,B,S0,S]
% (
% some [SA] p(A,S0,SA), p(B,SA,S)
% <=>
% some [SB] p(B,S0,SB), p(A,SB,S)
% )
% and given the actual variables, Vs, to the call to p, return
% the pair of variables which are state variables, SPair.
%
:- pred assertion__is_update_assertion(assert_id::in, module_info::in,
pred_id::in, prog_vars::in, pair(prog_var)::out) is semidet.
%
% assertion__is_construction_equivalence_assertion(Id, MI, 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 assertion__is_construction_equivalence_assertion(assert_id::in,
module_info::in, cons_id::in, pred_id::in) is semidet.
%
% assertion__in_interface_check
%
% Ensure that an assertion which is defined in an interface
% doesn't refer to any constructors, functions and predicates
% defined in the implementation of that module.
%
:- pred assertion__in_interface_check(hlds_goal::in, pred_info::in,
module_info::in, module_info::out,
io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module globals, goal_util, hlds_out.
:- import_module options, prog_out, prog_util, type_util.
:- import_module assoc_list, bool, list, map, require, set, std_util.
:- type subst == map(prog_var, prog_var).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__is_commutativity_assertion(AssertId, Module, CallVars,
CommutativeVars) :-
assertion__goal(AssertId, Module, Goal),
equivalent(Goal, P, Q),
P = call(PredId, _, VarsP, _, _, _) - _,
Q = 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.
% e.g [A,B,C] and [B,A,C] is true.
% It also takes a list of variables, Vs, to a call and returns
% the two variables in that list that can be swapped, ie [A,B].
%
:- pred commutative_var_ordering(prog_vars::in, prog_vars::in,
prog_vars::in, pair(prog_var)::out) is semidet.
commutative_var_ordering([P|Ps], [Q|Qs], [V|Vs], CommutativeVars) :-
(
P = Q
->
commutative_var_ordering(Ps, Qs, Vs, CommutativeVars)
;
commutative_var_ordering_2(P, Q, Ps, Qs, Vs, CallVarB),
CommutativeVars = V - CallVarB
).
:- pred commutative_var_ordering_2(prog_var::in, prog_var::in, prog_vars::in,
prog_vars::in, prog_vars::in, prog_var::out) is semidet.
commutative_var_ordering_2(VarP, VarQ, [P|Ps], [Q|Qs], [V|Vs], CallVarB) :-
(
P = Q
->
commutative_var_ordering_2(VarP, VarQ, Ps, Qs, Vs, CallVarB)
;
CallVarB = V,
P = VarQ,
Q = VarP,
Ps = Qs
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__is_associativity_assertion(AssertId, Module, CallVars,
AssociativeVars, OutputVar) :-
assertion__goal(AssertId, Module, Goal - GoalInfo),
equivalent(Goal - GoalInfo, P, Q),
goal_info_get_nonlocals(GoalInfo, UniversiallyQuantifiedVars),
% There may or may not be a some [] depending on whether
% the user explicity qualified the call or not.
(
P = some(_, _, conj(PCalls0) - _) - _PGoalInfo,
Q = some(_, _, conj(QCalls0) - _) - _QGoalInfo
->
PCalls = PCalls0,
QCalls = QCalls0
;
P = conj(PCalls) - _PGoalInfo,
Q = conj(QCalls) - _QGoalInfo
),
AssociativeVars - OutputVar =
promise_only_solution(associative(PCalls, QCalls,
UniversiallyQuantifiedVars, CallVars)).
% 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(prog_var)::in, prog_vars::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 nondet.
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.
%
% ie 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(prog_var)::in, pred_id::out,
prog_var::out, assoc_list(prog_var)::out,
prog_vars::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 their is only 3 arguments left.
list__filter((pred(X-Y::in) is semidet :- not X = Y), Vars0, Vars),
list__length(Vars, number_of_associative_vars).
:- func number_of_associative_vars = int.
number_of_associative_vars = 3.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% assertion__is_update_assertion(Id, MI, 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 the same state is produced as input regardless of
% the order that the state is updated.
%
% ie 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)))
% ).
%
assertion__is_update_assertion(AssertId, Module, _PredId, CallVars,
StateA - StateB) :-
assertion__goal(AssertId, Module, Goal - GoalInfo),
equivalent(Goal - GoalInfo, P, Q),
goal_info_get_nonlocals(GoalInfo, UniversiallyQuantifiedVars),
% There may or may not be an explicit some [Vars] there,
% as quantification now works correctly.
(
P = some(_, _, conj(PCalls0) - _) - _PGoalInfo,
Q = some(_, _, conj(QCalls0) - _) - _QGoalInfo
->
PCalls = PCalls0,
QCalls = QCalls0
;
P = conj(PCalls) - _PGoalInfo,
Q = conj(QCalls) - _QGoalInfo
),
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(prog_var)::in,
prog_vars::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(prog_var)::in,
pred_id::out, prog_var::out,
assoc_list(prog_var)::out, prog_vars::out) is semidet.
process_two_linked_calls(Goals, UniversiallyQuantifiedVars, PredId,
LinkingVar, Vars, VarsA) :-
Goals = [call(PredId, _, VarsA, _, _, _) - _,
call(PredId, _, VarsB, _, _, _) - _],
% Determine the linking variable, L.
% By definition it must be existentially quantified and
% a member of both variable lists.
CommonVars = list_to_set(VarsA) `intersect` list_to_set(VarsB),
set__singleton_set(CommonVars `difference` UniversiallyQuantifiedVars,
LinkingVar),
% Set up mapping between the variables in the two calls.
assoc_list__from_corresponding_lists(VarsA, VarsB, Vars).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__is_construction_equivalence_assertion(AssertId, Module,
ConsId, PredId) :-
assertion__goal(AssertId, Module, Goal),
equivalent(Goal, P, Q),
(
single_construction(P, ConsId)
->
predicate_call(Q, PredId)
;
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(unify(_, UnifyRhs, _, _, _) - _,
cons(QualifiedSymName, Arity)) :-
UnifyRhs = functor(cons(UnqualifiedSymName, Arity), _),
match_sym_name(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) :-
(
Goal = conj(Goals) - _
->
list__member(Call, Goals),
Call = call(PredId, _, _, _, _, _) - _,
list__delete(Goals, Call, Unifications),
P = (pred(G::in) is semidet :-
not (
G = unify(_, UnifyRhs, _, _, _) - _,
UnifyRhs = functor(_, _)
)
),
list__filter(P, Unifications, [])
;
Goal = call(PredId, _, _, _, _, _) - _
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__goal(AssertId, Module, Goal) :-
module_info_assertion_table(Module, AssertTable),
assertion_table_lookup(AssertTable, AssertId, PredId),
module_info_pred_info(Module, PredId, PredInfo),
pred_info_clauses_info(PredInfo, ClausesInfo),
clauses_info_clauses(ClausesInfo, Clauses),
(
Clauses = [clause(_ProcIds, Goal0, _Context)]
->
assertion__normalise_goal(Goal0, Goal)
;
error("assertion__goal: not an assertion")
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred implies(hlds_goal::in, hlds_goal::out, hlds_goal::out) is semidet.
implies(Goal, P, Q) :-
% Goal = (P => Q)
Goal = not(conj(GoalList) - GI) - _,
list__reverse(GoalList) = [NotQ | Ps],
(
Ps = [P0]
->
P = P0
;
P = conj(list__reverse(Ps)) - GI
),
NotQ = not(Q) - _.
:- pred equivalent(hlds_goal::in, hlds_goal::out, hlds_goal::out) is semidet.
equivalent(Goal, P, Q) :-
% Goal = P <=> Q
Goal = conj([A, B]) - _GoalInfo,
map__init(Subst),
implies(A, PA, QA),
implies(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(conj(GoalAs) - _, conj(GoalBs) - _, Subst0, Subst) :-
equal_goals_list(GoalAs, GoalBs, Subst0, Subst).
equal_goals(call(PredId, _, VarsA, _, _, _) - _,
call(PredId, _, VarsB, _, _, _) - _, Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst).
equal_goals(generic_call(Type, VarsA, _, _) - _,
generic_call(Type, VarsB, _, _) - _, Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst).
equal_goals(switch(Var, CanFail, CasesA, _) - _,
switch(Var, CanFail, CasesB, _) - _, Subst0, Subst) :-
equal_goals_cases(CasesA, CasesB, Subst0, Subst).
equal_goals(unify(VarA, RHSA, _, _, _) - _, unify(VarB, RHSB, _, _, _) - _,
Subst0, Subst) :-
equal_vars([VarA], [VarB], Subst0, Subst1),
equal_unification(RHSA, RHSB, Subst1, Subst).
equal_goals(disj(GoalAs, _) - _, disj(GoalBs, _) - _, Subst0, Subst) :-
equal_goals_list(GoalAs, GoalBs, Subst0, Subst).
equal_goals(not(GoalA) - _, not(GoalB) - _, Subst0, Subst) :-
equal_goals(GoalA, GoalB, Subst0, Subst).
equal_goals(some(VarsA, _, GoalA) - _, some(VarsB, _, GoalB) - _,
Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst1),
equal_goals(GoalA, GoalB, Subst1, Subst).
equal_goals(if_then_else(VarsA, IfA, ThenA, ElseA, _) - _,
if_then_else(VarsB, IfB, ThenB, ElseB, _) - _, Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst1),
equal_goals(IfA, IfB, Subst1, Subst2),
equal_goals(ThenA, ThenB, Subst2, Subst3),
equal_goals(ElseA, ElseB, Subst3, Subst).
equal_goals(pragma_c_code(Attribs, PredId, _, VarsA, _, _, _) - _,
pragma_c_code(Attribs, PredId, _, VarsB, _, _, _) - _,
Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst).
equal_goals(par_conj(GoalAs, _) - _, par_conj(GoalBs, _) - _, Subst0, Subst) :-
equal_goals_list(GoalAs, GoalBs, Subst0, Subst).
equal_goals(bi_implication(LeftGoalA, RightGoalA) - _,
bi_implication(LeftGoalB, RightGoalB) - _, Subst0, Subst) :-
equal_goals(LeftGoalA, LeftGoalB, Subst0, Subst1),
equal_goals(RightGoalA, RightGoalB, Subst1, Subst).
:- pred equal_vars(prog_vars::in, prog_vars::in, subst::in,
subst::out) is semidet.
equal_vars([], [], Subst, Subst).
equal_vars([VA | VAs], [VB | VBs], Subst0, Subst) :-
(
map__search(Subst0, VA, SubstVA)
->
SubstVA = VB,
equal_vars(VAs, VBs, Subst0, Subst)
;
map__insert(Subst0, VA, VB, Subst1),
equal_vars(VAs, VBs, Subst1, Subst)
).
:- pred equal_unification(unify_rhs::in, unify_rhs::in, subst::in,
subst::out) is semidet.
equal_unification(var(A), var(B), Subst0, Subst) :-
equal_vars([A], [B], Subst0, Subst).
equal_unification(functor(ConsId, VarsA), functor(ConsId, VarsB),
Subst0, Subst) :-
equal_vars(VarsA, VarsB, Subst0, Subst).
equal_unification(lambda_goal(PredOrFunc, EvalMethod, FixModes, NLVarsA, LVarsA,
Modes, Det, GoalA),
lambda_goal(PredOrFunc, EvalMethod, FixModes, NLVarsB, LVarsB,
Modes, Det, GoalB),
Subst0, Subst) :-
equal_vars(NLVarsA, NLVarsB, Subst0, Subst1),
equal_vars(LVarsA, LVarsB, Subst1, Subst2),
equal_goals(GoalA, GoalB, Subst2, Subst).
:- pred equal_goals_list(hlds_goals::in, hlds_goals::in, subst::in,
subst::out) is semidet.
equal_goals_list([], [], Subst, Subst).
equal_goals_list([GoalA | GoalAs], [GoalB | GoalBs], Subst0, Subst) :-
equal_goals(GoalA, GoalB, Subst0, Subst1),
equal_goals_list(GoalAs, GoalBs, Subst1, Subst).
:- pred equal_goals_cases(list(case)::in, list(case)::in, subst::in,
subst::out) is semidet.
equal_goals_cases([], [], Subst, Subst).
equal_goals_cases([CaseA | CaseAs], [CaseB | CaseBs], Subst0, Subst) :-
CaseA = case(ConsId, GoalA),
CaseB = case(ConsId, GoalB),
equal_goals(GoalA, GoalB, Subst0, Subst1),
equal_goals_cases(CaseAs, CaseBs, Subst1, Subst).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__record_preds_used_in(Goal, AssertId, Module0, Module) :-
% Explicit lambda expression needed since
% goal_calls_pred_id has multiple modes.
P = (pred(PredId::out) is nondet :- goal_calls_pred_id(Goal, PredId)),
solutions(P, PredIds),
list__foldl(update_pred_info(AssertId), PredIds, Module0, Module).
%-----------------------------------------------------------------------------%
%
% update_pred_info(Id, A, M0, M)
%
% 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, Module0, Module) :-
module_info_pred_info(Module0, PredId, PredInfo0),
pred_info_get_assertions(PredInfo0, Assertions0),
set__insert(Assertions0, AssertId, Assertions),
pred_info_set_assertions(PredInfo0, Assertions, PredInfo),
module_info_set_pred_info(Module0, PredId, PredInfo, Module).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% assertion__normalise_goal
%
% Place a hlds_goal into a standard form. Currently all the
% code does is replace conj([G]) with G.
%
:- pred assertion__normalise_goal(hlds_goal::in, hlds_goal::out) is det.
assertion__normalise_goal(call(A,B,C,D,E,F) - GI, call(A,B,C,D,E,F) - GI).
assertion__normalise_goal(generic_call(A,B,C,D) - GI, generic_call(A,B,C,D)-GI).
assertion__normalise_goal(unify(A,B,C,D,E) - GI, unify(A,B,C,D,E) - GI).
assertion__normalise_goal(pragma_c_code(A,B,C,D,E,F,G) - GI,
pragma_c_code(A,B,C,D,E,F,G) - GI).
assertion__normalise_goal(conj(Goals0) - GI, conj(Goals) - GI) :-
assertion__normalise_conj(Goals0, Goals).
assertion__normalise_goal(switch(A,B,Case0s,D) - GI, switch(A,B,Cases,D)-GI) :-
assertion__normalise_cases(Case0s, Cases).
assertion__normalise_goal(disj(Goal0s,B) - GI, disj(Goals,B) - GI) :-
assertion__normalise_goals(Goal0s, Goals).
assertion__normalise_goal(not(Goal0) - GI, not(Goal) - GI) :-
assertion__normalise_goal(Goal0, Goal).
assertion__normalise_goal(some(A,B,Goal0) - GI, some(A,B,Goal) - GI) :-
assertion__normalise_goal(Goal0, Goal).
assertion__normalise_goal(if_then_else(A, If0, Then0, Else0, E) - GI,
if_then_else(A, If, Then, Else, E) - GI) :-
assertion__normalise_goal(If0, If),
assertion__normalise_goal(Then0, Then),
assertion__normalise_goal(Else0, Else).
assertion__normalise_goal(par_conj(Goal0s,B) - GI, par_conj(Goals,B) - GI) :-
assertion__normalise_goals(Goal0s, Goals).
assertion__normalise_goal(bi_implication(LHS0, RHS0) - GI,
bi_implication(LHS, RHS) - GI) :-
assertion__normalise_goal(LHS0, LHS),
assertion__normalise_goal(RHS0, RHS).
%-----------------------------------------------------------------------------%
:- pred assertion__normalise_conj(hlds_goals::in, hlds_goals::out) is det.
assertion__normalise_conj([], []).
assertion__normalise_conj([Goal0 | Goal0s], Goals) :-
goal_to_conj_list(Goal0, ConjGoals),
assertion__normalise_conj(Goal0s, Goal1s),
list__append(ConjGoals, Goal1s, Goals).
:- pred assertion__normalise_cases(list(case)::in, list(case)::out) is det.
assertion__normalise_cases([], []).
assertion__normalise_cases([Case0 | Case0s], [Case | Cases]) :-
Case0 = case(ConsId, Goal0),
assertion__normalise_goal(Goal0, Goal),
Case = case(ConsId, Goal),
assertion__normalise_cases(Case0s, Cases).
:- pred assertion__normalise_goals(hlds_goals::in, hlds_goals::out) is det.
assertion__normalise_goals([], []).
assertion__normalise_goals([Goal0 | Goal0s], [Goal | Goals]) :-
assertion__normalise_goal(Goal0, Goal),
assertion__normalise_goals(Goal0s, Goals).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
assertion__in_interface_check(call(PredId,_,_,_,_,SymName) - GoalInfo,
_PredInfo, Module0, Module) -->
{ module_info_pred_info(Module0, PredId, CallPredInfo) },
{ pred_info_import_status(CallPredInfo, ImportStatus) },
(
{ is_defined_in_implementation_section(ImportStatus, yes) }
->
{ goal_info_get_context(GoalInfo, Context) },
{ pred_info_get_is_pred_or_func(CallPredInfo, PredOrFunc) },
{ pred_info_arity(CallPredInfo, Arity) },
write_assertion_interface_error(Context,
call(PredOrFunc, SymName, Arity),
Module0, Module)
;
{ Module = Module0 }
).
assertion__in_interface_check(generic_call(_,_,_,_) - _, _,
Module, Module) --> [].
assertion__in_interface_check(unify(Var,RHS,_,_,_) - GoalInfo,
PredInfo, Module0, Module) -->
{ goal_info_get_context(GoalInfo, Context) },
assertion__in_interface_check_unify_rhs(RHS, Var, Context,
PredInfo, Module0, Module).
assertion__in_interface_check(pragma_c_code(_,PredId,_,_,_,_,_) - GoalInfo,
_PredInfo, Module0, Module) -->
{ module_info_pred_info(Module0, PredId, PragmaPredInfo) },
{ pred_info_import_status(PragmaPredInfo, ImportStatus) },
(
{ is_defined_in_implementation_section(ImportStatus, yes) }
->
{ goal_info_get_context(GoalInfo, Context) },
{ pred_info_get_is_pred_or_func(PragmaPredInfo, PredOrFunc) },
{ pred_info_name(PragmaPredInfo, Name) },
{ SymName = unqualified(Name) },
{ pred_info_arity(PragmaPredInfo, Arity) },
write_assertion_interface_error(Context,
call(PredOrFunc, SymName, Arity),
Module0, Module)
;
{ Module = Module0 }
).
assertion__in_interface_check(conj(Goals) - _, PredInfo, Module0, Module) -->
assertion__in_interface_check_list(Goals, PredInfo, Module0, Module).
assertion__in_interface_check(switch(_,_,_,_) - _, _, _, _) -->
{ error("assertion__in_interface_check: assertion contains switch.") }.
assertion__in_interface_check(disj(Goals,_) - _, PredInfo, Module0, Module) -->
assertion__in_interface_check_list(Goals, PredInfo, Module0, Module).
assertion__in_interface_check(not(Goal) - _, PredInfo, Module0, Module) -->
assertion__in_interface_check(Goal, PredInfo, Module0, Module).
assertion__in_interface_check(some(_,_,Goal) - _, PredInfo, Module0, Module) -->
assertion__in_interface_check(Goal, PredInfo, Module0, Module).
assertion__in_interface_check(if_then_else(_, If, Then, Else, _) - _,
PredInfo, Module0, Module) -->
assertion__in_interface_check(If, PredInfo, Module0, Module1),
assertion__in_interface_check(Then, PredInfo, Module1, Module2),
assertion__in_interface_check(Else, PredInfo, Module2, Module).
assertion__in_interface_check(par_conj(Goals,_) - _, PredInfo,
Module0, Module) -->
assertion__in_interface_check_list(Goals, PredInfo, Module0, Module).
assertion__in_interface_check(bi_implication(LHS, RHS) - _, PredInfo,
Module0, Module) -->
assertion__in_interface_check(LHS, PredInfo, Module0, Module1),
assertion__in_interface_check(RHS, PredInfo, Module1, Module).
%-----------------------------------------------------------------------------%
:- pred assertion__in_interface_check_unify_rhs(unify_rhs::in, prog_var::in,
prog_context::in, pred_info::in, module_info::in,
module_info::out, io__state::di, io__state::uo) is det.
assertion__in_interface_check_unify_rhs(var(_), _, _, _, Module, Module) --> [].
assertion__in_interface_check_unify_rhs(functor(ConsId, _), Var, Context,
PredInfo, Module0, Module) -->
{ pred_info_clauses_info(PredInfo, ClausesInfo) },
{ clauses_info_vartypes(ClausesInfo, VarTypes) },
{ map__lookup(VarTypes, Var, Type) },
(
{ type_to_type_id(Type, TypeId, _) }
->
{ module_info_types(Module0, Types) },
{ map__lookup(Types, TypeId, TypeDefn) },
{ hlds_data__get_type_defn_status(TypeDefn, TypeStatus) },
(
{ is_defined_in_implementation_section(TypeStatus,
yes) }
->
write_assertion_interface_error(Context,
cons(ConsId), Module0, Module)
;
{ Module = Module0 }
)
;
{ error("assertion__in_interface_check_unify_rhs: type_to_type_id failed.") }
).
assertion__in_interface_check_unify_rhs(lambda_goal(_,_,_,_,_,_,_,Goal),
_Var, _Context, PredInfo, Module0, Module) -->
assertion__in_interface_check(Goal, PredInfo, Module0, Module).
%-----------------------------------------------------------------------------%
:- pred assertion__in_interface_check_list(hlds_goals::in, pred_info::in,
module_info::in, module_info::out,
io__state::di, io__state::uo)is det.
assertion__in_interface_check_list([], _, Module, Module) --> [].
assertion__in_interface_check_list([Goal0 | Goal0s], PredInfo,
Module0, Module) -->
assertion__in_interface_check(Goal0, PredInfo, Module0, Module1),
assertion__in_interface_check_list(Goal0s, PredInfo, Module1, Module).
%-----------------------------------------------------------------------------%
%
% is_defined_in_implementation_section
%
% Returns yes if the import_status indicates the item came form
% the implementation section.
%
:- pred is_defined_in_implementation_section(import_status::in,
bool::out) is det.
is_defined_in_implementation_section(abstract_exported, yes).
is_defined_in_implementation_section(exported_to_submodules, yes).
is_defined_in_implementation_section(local, yes).
is_defined_in_implementation_section(imported(implementation), yes).
is_defined_in_implementation_section(imported(interface), no).
is_defined_in_implementation_section(opt_imported, no).
is_defined_in_implementation_section(abstract_imported, no).
is_defined_in_implementation_section(pseudo_imported, no).
is_defined_in_implementation_section(exported, no).
is_defined_in_implementation_section(pseudo_exported, no).
%-----------------------------------------------------------------------------%
:- type call_or_consid
---> call(pred_or_func, sym_name, arity)
; cons(cons_id).
:- pred write_assertion_interface_error(prog_context::in,
call_or_consid::in, module_info::in, module_info::out,
io__state::di, io__state::uo) is det.
write_assertion_interface_error(Context, Type, Module0, Module) -->
{ module_info_incr_errors(Module0, Module) },
{ module_info_name(Module, ModuleName) },
prog_out__write_context(Context),
io__write_string("In interface for module `"),
prog_out__write_sym_name(ModuleName),
io__write_string("':\n"),
prog_out__write_context(Context),
io__write_string(" error: exported promise refers to "),
(
{ Type = call(PredOrFunc, SymName, Arity) },
hlds_out__write_simple_call_id(PredOrFunc, SymName, Arity),
io__nl
;
{ Type = cons(ConsId) },
io__write_string("constructor `"),
hlds_out__write_cons_id(ConsId),
io__write_string("'\n")
),
prog_out__write_context(Context),
io__write_string(" which is defined in the implementation "),
io__write_string("of module `"),
prog_out__write_sym_name(ModuleName),
io__write_string("'.\n"),
globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
(
{ VerboseErrors = yes }
->
prog_out__write_context(Context),
io__write_string(" Either move the promise into the "),
io__write_string("implementation section\n"),
prog_out__write_context(Context),
io__write_string(" or move the definition "),
io__write_string("into the interface.\n")
;
[]
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%