mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
The new --warn-unneeded-initial-statevar option asks the compiler
to warn about code such as
pred_a(!.X, ...) :-
... code that uses !.X, but does not update it ...
In this case, the preferred fix is to just replace all occurrences
of !.X with X.
The new --warn-unneeded-final-statevar option asks the compiler
to warn about code such as
pred_a(!X, ...) :-
... code that maybe uses !.X, but does not update it ...
In this case, the preferred fix also involves replacing all occurrences
of !.X with X, but it also involves either deleting the argument
containing !:X (the best option), or, if there is some reason why
the predicate's signature must stay unchanged, to replace !:X with X as well.
And if the clause body does not actually refer to either !.X or !:X, then
*both* arguments represented by !X should be deleted.
The first option is a style warning; the second option, due to the
signature change it may call for, is a non-style warning.
Both options have a version whose name adds a "-lambda" suffix, and which
does the same warnings for the heads of lambda expressions, not clauses.
Note that several of the modules below, including some that help to implement
the warnings, also contain code changes that result from *acting* on
the new warnings, e.g. by deleting unneeded statevar arguments.
Other, similar changes will also come after this diff is committed.
compiler/options.m:
doc/user_guide.texi:
Document the new options.
compiler/state_var.m:
Gather the information needed to decide what code merits the new warnings.
Do so in three stages:
- when processing the head of a clause or of a lambda expression,
- when processing the body goal of that clause or lambda expression,
- when finishing up the processing of the clause or lambda expression.
Add a predicate to generate the warnings for lambda expressions.
Do not generate the warnings for clauses. This is because whether or not
we want to warn about state vars in some clauses depends on the properties
of *other* clauses of the same predicate, and state_var.m has access
to only one clause at a time. Instead,
- return the info needed by the warning-generating code in pre_typecheck.m
(one of the first passes we execute after adding all clauses
to the HLDS), and
- we export some functionality for use by that code.
Switch to a convention for naming the program variables corresponding
to the middle (non-initial, non-final) versions of state variables
whose output is affected by changes in the code of the clause body goal
only if they involve that specific state variable.
Give some predicates more descriptive names.
compiler/make_hlds.m:
Make state_var.m and its new functionality visible from outside
the make_hlds package.
compiler/add_clause.m:
Record the information gathered by state_var.m in each clause.
compiler/hlds_clauses.m:
Add a slot to each clause for this information.
Give some predicates more descriptive names.
compiler/headvar_names.m:
Use the contents of the new slots to detect whether any clauses
have unused state vars, and if so, return the chosen consensus names
of the head vars to the code of pre_typecheck.m, which uses this info
as part of the implementation of the new warnings.
compiler/pre_typecheck.m:
Implement the new warnings.
compiler/mercury_compile_front_end.m:
Record the warnings that pre_typecheck.m can now return.
compiler/error_spec.m:
compiler/write_error_spec.m:
Add unsigned versions of the format pieces involving ints, for use
by the new code in pre_typecheck.m, and implement them.
compiler/hlds_out_util.m:
compiler/maybe_util.m:
Move two related types from hlds_out_util.m to maybe_util.m,
in order to allow pre_typecheck.m to use one of them.
compiler/hlds_args.m:
Add a new utility function for use by the new code above.
compiler/foreign.m:
Act on the new warnings by deleting the long-unused predicates
being warned about.
compiler/post_typecheck.m:
Speed up this traversal. (I originally thought to implement
the new warnings in this pass.)
compiler/add_foreign_proc.m:
compiler/add_pragma.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_type.m:
compiler/build_mode_constraints.m:
compiler/call_gen.m:
compiler/check_typeclass.m:
compiler/clause_to_proc.m:
compiler/code_loc_dep.m:
compiler/delay_info.m:
compiler/delay_partial_inst.m:
compiler/dense_switch.m:
compiler/det_check_goal.m:
compiler/det_infer_goal.m:
compiler/disj_gen.m:
compiler/du_type_layout.m:
compiler/format_call.m:
compiler/goal_expr_to_goal.m:
compiler/hlds_dependency_graph.m:
compiler/hlds_out_pred.m:
compiler/hlds_pred.m:
compiler/hlds_rtti.m:
compiler/inst_merge.m:
compiler/instance_method_clauses.m:
compiler/intermod.m:
compiler/interval.m:
compiler/ite_gen.m:
compiler/lookup_switch.m:
compiler/make_hlds_passes.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mode_errors.m:
compiler/parse_string_format.m:
compiler/passes_aux.m:
compiler/polymorphism.m:
compiler/polymorphism_info.m:
compiler/polymorphism_type_info.m:
compiler/pragma_c_gen.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/quantification.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_conj.m:
compiler/string_switch.m:
compiler/superhomogeneous.m:
compiler/switch_gen.m:
compiler/tag_switch.m:
compiler/type_constraints.m:
compiler/typecheck.m:
compiler/typecheck_clauses.m:
compiler/typecheck_coerce.m:
compiler/typecheck_error_unify.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_proc.m:
compiler/var_origins.m:
Conform to the changes above, and/or act on the new warnings.
browser/diff.m:
library/bit_buffer.m:
library/getopt.m:
library/getopt_io.m:
library/io.error_util.m:
library/io.file.m:
library/mercury_term_lexer.m:
library/parsing_utils.m:
library/pretty_printer.m:
library/robdd.m:
library/rtti_implementation.m:
library/string.builder.m:
library/string.parse_runtime.m:
mdbcomp/feedback.m:
Act on the new warnings.
tests/hard_coded/sv_nested_closures.m:
Change this test's code to avoid the new warnings, since
(if --halt-at-warn is ever enabled) the warnings would interfere
with its job .
tests/invalid/bug197.err_exp:
tests/invalid/bug487.err_exp:
tests/invalid/nullary_ho_func_error.err_exp:
tests/invalid/try_detism.err_exp:
tests/warnings/singleton_test_state_var.err_exp:
Expect variable names for the middle versions of state vars
using the new naming scheme.
582 lines
25 KiB
Mathematica
582 lines
25 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2004-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2018, 2020, 2022-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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: prop_mode_constraints.m.
|
|
% Main author: richardf.
|
|
%
|
|
% XXX This module essentially serves as interface between the old
|
|
% mode_constraints module (by dmo) and the new abstract_mode_constraints
|
|
% and build_mode_constraints modules (by richardf). Ultimately its contents
|
|
% should probably be moved into those modules respectively.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.prop_mode_constraints.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.abstract_mode_constraints.
|
|
:- import_module check_hlds.build_mode_constraints.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Maps each predicate to information about its goal ids and its mode
|
|
% constraint.
|
|
:- type pred_constraints_map ==
|
|
map(pred_id, {containing_goal_map, mode_constraints}).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% prop_mode_constraints_in_scc(SCC, !ModuleInfo, !VarSet, !VarMap,
|
|
% !PredConstraintsMap):
|
|
%
|
|
% For each predicate in SCC:
|
|
%
|
|
% - Add producer/consumer constraints to PredConstraintsMap.
|
|
% - Add goal path annotations to its clauses in ModuleInfo.
|
|
% - Add any constraint variables it requires to VarSet and VarMap.
|
|
%
|
|
:- pred prop_mode_constraints_in_scc(list(pred_id)::in,
|
|
module_info::in, module_info::out, mc_var_info::in, mc_var_info::out,
|
|
pred_constraints_map::in, pred_constraints_map::out) is det.
|
|
|
|
% ensure_unique_arguments(PredId, !ModuleInfo):
|
|
%
|
|
% Creates variables and introduces unifications in predicate PredId where
|
|
% appropriate to ensure that no program variable is used as an argument of
|
|
% more than one predicate (including the head of the caller, PredId).
|
|
%
|
|
:- pred ensure_unique_arguments(pred_id::in, module_info::in, module_info::out)
|
|
is det.
|
|
|
|
% Check whether a predicate has been imported according to the
|
|
% status_is_imported pred in the hlds_pred module.
|
|
%
|
|
:- pred module_info_pred_status_is_imported(module_info::in, pred_id::in)
|
|
is semidet.
|
|
|
|
% Write in human readable form to the current output stream
|
|
% the information in the pred_constraints_map, indicating which
|
|
% predicate each set of constraints applies to.
|
|
%
|
|
:- pred pretty_print_pred_constraints_map(io.text_output_stream::in,
|
|
module_info::in, mc_varset::in, pred_constraints_map::in,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.goal_path.
|
|
:- import_module hlds.hlds_args.
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_error_util.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.make_goal.
|
|
:- import_module hlds.status.
|
|
:- import_module hlds.var_table_hlds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.vartypes.
|
|
:- import_module parse_tree.write_error_spec.
|
|
|
|
:- import_module bool.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
prop_mode_constraints_in_scc(SCC0, !ModuleInfo, !VarInfo, !Constraints) :-
|
|
% Process only predicates from this module.
|
|
list.filter(module_info_pred_status_is_imported(!.ModuleInfo),
|
|
SCC0, _, SCC),
|
|
|
|
% Prepare the solver variables for the home path of each predicate
|
|
% of the SCC - needed for calls to predicates in the same SCC
|
|
% that do not have mode declarations.
|
|
add_mc_vars_for_scc_heads(!.ModuleInfo, SCC, !VarInfo),
|
|
|
|
% Now go through the SCC and add the constraint
|
|
% variables and then constraints predicate by predicate
|
|
list.foldl3(prop_mode_constraints_in_pred, SCC,
|
|
!ModuleInfo, !VarInfo, !Constraints).
|
|
|
|
% prop_mode_constraints_in_pred(PredId, !ModuleInfo, !VarSet, !VarMap,
|
|
% !Constraints):
|
|
%
|
|
% Performs a number of tasks for predicate PredId:
|
|
% 1) Fill out the goal_id information in the ModuleInfo structure.
|
|
% 2) Add producer/consumer constraint variables for program
|
|
% variables corresponding to any location at which they are
|
|
% nonlocal to VarSet and VarMap. (Elsewhere is is clear as to
|
|
% whether they are produced or consumed.)
|
|
% 3) Add mode declaration constraints to Constraints.
|
|
% 4) Add goal constraints to Constraints.
|
|
%
|
|
% NOTE: This relies on the head variables for any predicate without mode
|
|
% declarations that is called by this one (PredId) to have the constraint
|
|
% variables corresponding to the empty goal path (i.e. the whole body
|
|
% of the predicate) to already be VarMap (and therefore also in VarSet).
|
|
%
|
|
:- pred prop_mode_constraints_in_pred(pred_id::in,
|
|
module_info::in, module_info::out, mc_var_info::in, mc_var_info::out,
|
|
pred_constraints_map::in, pred_constraints_map::out) is det.
|
|
|
|
prop_mode_constraints_in_pred(PredId, !ModuleInfo, !VarInfo, !Constraints) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo0),
|
|
do_prop_mode_constraints_in_pred(!.ModuleInfo, PredId, PredInfo0, PredInfo,
|
|
!VarInfo, !Constraints),
|
|
module_info_set_pred_info(PredId, PredInfo, !ModuleInfo).
|
|
|
|
% The working part of prop_mode_constraints_in_pred/8, with the pred_info
|
|
% unpacked from the ModuleInfo.
|
|
%
|
|
:- pred do_prop_mode_constraints_in_pred(module_info::in, pred_id::in,
|
|
pred_info::in, pred_info::out, mc_var_info::in, mc_var_info::out,
|
|
pred_constraints_map::in, pred_constraints_map::out) is det.
|
|
|
|
do_prop_mode_constraints_in_pred(ModuleInfo, PredId, !PredInfo, !VarInfo,
|
|
!PredConstraintsMap) :-
|
|
% XXX Currently the constraints simply say that if a variable is bound
|
|
% at a disjunct it is bound at the disjunction by making the relevant
|
|
% variables equivalent. Setting GoalPathOptimisation to yes will cause
|
|
% the disjucts to be given the same path as the disjunction, so that
|
|
% the relevant constraint variables will not need to be constrained
|
|
% equivalent - they will be the same variable. It will do the same
|
|
% for other path types with similar equivalence constraints -
|
|
% refer to the goal_path module for a more detailed description.
|
|
% GoalPathOptimisation = no.
|
|
%
|
|
% XXX The above comment is obsolete, in that the current version of the
|
|
% goal_path module leaves the task of making the relevant variables
|
|
% equivalent to THIS module.
|
|
|
|
pred_info_get_clauses_info(!.PredInfo, ClausesInfo0),
|
|
fill_goal_id_slots_in_clauses(ModuleInfo, ContainingGoalMap,
|
|
ClausesInfo0, ClausesInfo),
|
|
pred_info_set_clauses_info(ClausesInfo, !PredInfo),
|
|
|
|
% If mode inference requested, just add constraints for the clause body,
|
|
% otherwise, process the predicate for each of the procedures.
|
|
|
|
( if pred_info_infer_modes(!.PredInfo) then
|
|
add_clauses_constraints(ModuleInfo, PredId, !.PredInfo, !VarInfo,
|
|
init_pred_p_c_constraints, BodyConstraints)
|
|
else
|
|
prop_mode_constraints_in_mode_declared_pred(ModuleInfo, PredId,
|
|
!.PredInfo, !VarInfo, BodyConstraints)
|
|
),
|
|
map.set(PredId, {ContainingGoalMap, BodyConstraints},
|
|
!PredConstraintsMap).
|
|
|
|
% prop_mode_constraints_in_mode_declared_pred(ModuleInfo, PredId, PredInfo,
|
|
% !VarInfo, FullConstraints):
|
|
%
|
|
% Uses the clauses and mode declarations in PredInfo (which should be
|
|
% for predicate PredId taken from ModuleInfo) to create producer/consumer
|
|
% constraints for program variables in predicate PredId. VarInfo is updated
|
|
% with any constraint variables used.
|
|
%
|
|
:- pred prop_mode_constraints_in_mode_declared_pred(module_info::in,
|
|
pred_id::in, pred_info::in, mc_var_info::in, mc_var_info::out,
|
|
mode_constraints::out) is det.
|
|
|
|
prop_mode_constraints_in_mode_declared_pred(ModuleInfo, PredId, PredInfo,
|
|
!VarInfo, FullConstraints) :-
|
|
ProcIds = pred_info_all_procids(PredInfo),
|
|
|
|
add_clauses_constraints(ModuleInfo, PredId, PredInfo, !VarInfo,
|
|
init_pred_p_c_constraints, BodyConstr),
|
|
|
|
% Store procedure specific constraints in the constraints structure
|
|
list.map(pred_info_proc_info(PredInfo), ProcIds, ProcInfos),
|
|
list.foldl2_corresponding(
|
|
prop_mode_constraints_in_mode_declared_proc(ModuleInfo, PredId),
|
|
ProcIds, ProcInfos, !VarInfo, BodyConstr, FullConstraints).
|
|
|
|
% prop_mode_constraints_in_mode_declared_proc(ModuleInfo, PredId, ProcId,
|
|
% ProcInfo, !VarInfo, !PredConstraints):
|
|
%
|
|
% Adds constraints based on the mode declaration in ProcInfo to
|
|
% the PredConstraints structure, associating them specifically with ProcId.
|
|
% Relies on the constraint variables associated with the head variables
|
|
% of PredId at the empty goal path being stored in VarInfo.
|
|
%
|
|
:- pred prop_mode_constraints_in_mode_declared_proc(module_info::in,
|
|
pred_id::in, proc_id::in, proc_info::in, mc_var_info::in,
|
|
mc_var_info::out, mode_constraints::in, mode_constraints::out) is det.
|
|
|
|
prop_mode_constraints_in_mode_declared_proc(ModuleInfo, PredId, ProcId,
|
|
ProcInfo, !VarInfo, !PredConstraints) :-
|
|
proc_info_get_argmodes(ProcInfo, ArgModes),
|
|
proc_info_get_headvars(ProcInfo, Args),
|
|
add_mode_decl_constraints(ModuleInfo, PredId, ProcId, ArgModes, Args,
|
|
!VarInfo, !PredConstraints).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
ensure_unique_arguments(PredId, !ModuleInfo) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo0),
|
|
pred_info_get_clauses_info(PredInfo0, ClausesInfo0),
|
|
clauses_info_get_clauses_rep(ClausesInfo0, ClausesRep0, ItemNumbers),
|
|
clauses_info_get_var_table(ClausesInfo0, VarTable0),
|
|
split_var_table(VarTable0, VarSet0, VarTypes0),
|
|
clauses_info_get_arg_vector(ClausesInfo0, ArgVector),
|
|
|
|
SeenSoFar = set_of_var.list_to_set(proc_arg_vector_to_list(ArgVector)),
|
|
get_clause_list_for_replacement(ClausesRep0, Clauses0),
|
|
BodyGoals0 = list.map(func(X) = clause_body(X), Clauses0),
|
|
list.map_foldl3(ensure_unique_arguments_in_goal, BodyGoals0, BodyGoals,
|
|
SeenSoFar, _, VarSet0, VarSet, VarTypes0, VarTypes),
|
|
|
|
Clauses = list.map_corresponding(func(C, B) = C ^ clause_body := B,
|
|
Clauses0, BodyGoals),
|
|
set_clause_list(Clauses, ClausesRep),
|
|
some [!ClausesInfo] (
|
|
!:ClausesInfo = ClausesInfo0,
|
|
make_var_table(!.ModuleInfo, VarSet, VarTypes, VarTable),
|
|
clauses_info_set_var_table(VarTable, !ClausesInfo),
|
|
clauses_info_set_clauses_rep(ClausesRep, ItemNumbers, !ClausesInfo),
|
|
pred_info_set_clauses_info(!.ClausesInfo, PredInfo0, PredInfo)
|
|
),
|
|
module_info_set_pred_info(PredId, PredInfo, !ModuleInfo).
|
|
|
|
% ensure_unique_arguments_in_goal(!Goal, !SeenSoFar, !VarSet, !VarTypes):
|
|
%
|
|
% Create variables and introduce unifications in Goal, where appropriate,
|
|
% to ensure that no program variable in SeenSoFar is used as an argument in
|
|
% a predicate call and that no program variable is used as an argument for
|
|
% more than one predicate call.
|
|
% Created variables are added to VarSet, VarTypes and SeenSoFar. Variables
|
|
% used as arguments in predicate calls are added to SeenSoFar.
|
|
%
|
|
:- pred ensure_unique_arguments_in_goal(hlds_goal::in, hlds_goal::out,
|
|
set_of_progvar::in, set_of_progvar::out, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out) is det.
|
|
|
|
ensure_unique_arguments_in_goal(!Goal, !SeenSoFar, !VarSet, !VarTypes) :-
|
|
some [!GoalExpr, !GoalInfo] (
|
|
!.Goal = hlds_goal(!:GoalExpr, !:GoalInfo),
|
|
(
|
|
!.GoalExpr = conj(ConjType, Goals0),
|
|
(
|
|
ConjType = plain_conj,
|
|
list.map_foldl3(ensure_unique_arguments_in_goal,
|
|
Goals0, Goals1, !SeenSoFar, !VarSet, !VarTypes),
|
|
flatten_conjunction(Goals1, Goals),
|
|
!:GoalExpr = conj(plain_conj, Goals)
|
|
;
|
|
ConjType = parallel_conj,
|
|
list.map_foldl3(ensure_unique_arguments_in_goal,
|
|
Goals0, Goals, !SeenSoFar, !VarSet, !VarTypes),
|
|
!:GoalExpr = conj(parallel_conj, Goals)
|
|
)
|
|
;
|
|
!.GoalExpr = plain_call(CalleePredId, CalleeProcId, Args0,
|
|
Builtin, UnifyContext, SymName),
|
|
Context = goal_info_get_context(!.GoalInfo),
|
|
make_unifications(Context, Unifications, Args0, Args, !SeenSoFar,
|
|
!VarSet, !VarTypes),
|
|
(
|
|
% No arguments changed.
|
|
Unifications = []
|
|
;
|
|
% Some of the argument variables have been replaced.
|
|
% Need to put the call with its new args in a conjunction
|
|
% with the unifications.
|
|
Unifications = [_ | _],
|
|
CallGoalExpr = plain_call(CalleePredId, CalleeProcId, Args,
|
|
Builtin, UnifyContext, SymName),
|
|
replace_call_with_conjunction(CallGoalExpr, Unifications,
|
|
Args, !:GoalExpr, !GoalInfo)
|
|
)
|
|
|
|
;
|
|
!.GoalExpr = generic_call(Details, Args0, Modes, MaybeArgRegs,
|
|
Determinism),
|
|
Context = goal_info_get_context(!.GoalInfo),
|
|
make_unifications(Context, Unifications, Args0, Args, !SeenSoFar,
|
|
!VarSet, !VarTypes),
|
|
(
|
|
% No arguments changed.
|
|
Unifications = []
|
|
;
|
|
% Some of the argument variables have been replaced.
|
|
% Need to put the call with its new args in a conjunction
|
|
% with the unifications.
|
|
Unifications = [_ | _],
|
|
CallGoalExpr = generic_call(Details, Args, Modes, MaybeArgRegs,
|
|
Determinism),
|
|
replace_call_with_conjunction(CallGoalExpr, Unifications,
|
|
Args, !:GoalExpr, !GoalInfo)
|
|
)
|
|
;
|
|
!.GoalExpr = switch(_SwitchVar, _CanFail, _Cases0)
|
|
% unify_proc.m can generate switches before this pass.
|
|
% unexpected($pred, "switch")
|
|
;
|
|
!.GoalExpr = unify(_, _, _, _, _)
|
|
;
|
|
!.GoalExpr = disj(Goals0),
|
|
list.map_foldl3(ensure_unique_arguments_in_goal, Goals0, Goals,
|
|
!SeenSoFar, !VarSet, !VarTypes),
|
|
!:GoalExpr = disj(Goals)
|
|
;
|
|
!.GoalExpr = negation(Goal0),
|
|
ensure_unique_arguments_in_goal(Goal0, Goal, !SeenSoFar, !VarSet,
|
|
!VarTypes),
|
|
!:GoalExpr = negation(Goal)
|
|
;
|
|
!.GoalExpr = scope(Reason, Goal0),
|
|
% XXX We should special-case the handling of
|
|
% from_ground_term_construct scopes.
|
|
ensure_unique_arguments_in_goal(Goal0, Goal, !SeenSoFar, !VarSet,
|
|
!VarTypes),
|
|
!:GoalExpr = scope(Reason, Goal)
|
|
;
|
|
!.GoalExpr = if_then_else(ExistVars, Cond0, Then0, Else0),
|
|
ensure_unique_arguments_in_goal(Cond0, Cond, !SeenSoFar, !VarSet,
|
|
!VarTypes),
|
|
ensure_unique_arguments_in_goal(Then0, Then, !SeenSoFar, !VarSet,
|
|
!VarTypes),
|
|
ensure_unique_arguments_in_goal(Else0, Else, !SeenSoFar, !VarSet,
|
|
!VarTypes),
|
|
!:GoalExpr = if_then_else(ExistVars, Cond, Then, Else)
|
|
;
|
|
!.GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
|
|
;
|
|
!.GoalExpr = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal0, OrElseGoals0, OrElseInners),
|
|
ensure_unique_arguments_in_goal(MainGoal0, MainGoal,
|
|
!SeenSoFar, !VarSet, !VarTypes),
|
|
list.map_foldl3(ensure_unique_arguments_in_goal,
|
|
OrElseGoals0, OrElseGoals, !SeenSoFar, !VarSet, !VarTypes),
|
|
ShortHand = atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners),
|
|
!:GoalExpr = shorthand(ShortHand)
|
|
;
|
|
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
|
|
ensure_unique_arguments_in_goal(SubGoal0, SubGoal, !SeenSoFar,
|
|
!VarSet, !VarTypes),
|
|
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
|
|
!:GoalExpr = shorthand(ShortHand)
|
|
;
|
|
ShortHand0 = bi_implication(_, _),
|
|
unexpected($pred, "bi_implication")
|
|
)
|
|
),
|
|
!:Goal = hlds_goal(!.GoalExpr, !.GoalInfo)
|
|
).
|
|
|
|
% flatten_conjunction(!Goals) flattens the conjunction Goals - that
|
|
% is, moves the conjuncts from nested conjunctions into Goals.
|
|
%
|
|
:- pred flatten_conjunction(list(hlds_goal)::in, list(hlds_goal)::out) is det.
|
|
|
|
flatten_conjunction(!Goals) :-
|
|
list.foldr(add_to_before_conjunction, !.Goals, [], !:Goals).
|
|
|
|
% add_to_before_conjunction(Goal, !Goals) adds Goal to the front of the
|
|
% conjunction Goals. It keeps the conjunction flat, so nested conjunctions
|
|
% are flattened and their conjuncts prepended to Goals.
|
|
%
|
|
:- pred add_to_before_conjunction(hlds_goal::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out) is det.
|
|
|
|
add_to_before_conjunction(Goal, !Goals) :-
|
|
( if Goal = hlds_goal(conj(plain_conj, SubGoals), _) then
|
|
!:Goals = SubGoals ++ !.Goals
|
|
else
|
|
!:Goals = [Goal | !.Goals]
|
|
).
|
|
|
|
% make_unifications(Context, MaybeUnifications, Args0, Args, !SeenSoFar,
|
|
% !VarSet, !VarTypes):
|
|
%
|
|
% If any of the given arguments in Args0 is in SeenSoFar, creates a new
|
|
% argument (in VarSet and VarTypes) to replace it (in Args), and generates
|
|
% a unification between new argument and old (with context Context).
|
|
%
|
|
:- pred make_unifications(prog_context::in, list(hlds_goal)::out,
|
|
list(prog_var)::in, list(prog_var)::out,
|
|
set_of_progvar::in, set_of_progvar::out,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out) is det.
|
|
|
|
make_unifications(Context, Unifications, !Args, !SeenSoFar,
|
|
!VarSet, !VarTypes) :-
|
|
list.map_foldl4(make_unification(Context), !Args,
|
|
[], Unifications, !SeenSoFar, !VarSet, !VarTypes).
|
|
|
|
% make_unification(Context, Var0, Var, !Unifications, !SeenSoFar,
|
|
% !VarSet, !VarTypes):
|
|
%
|
|
% If Var0 is in SeenSoFar, creates a new argument Var (in VarSet and
|
|
% VarTypes), and generates a unification between Var0 and Var.
|
|
%
|
|
:- pred make_unification(prog_context::in, prog_var::in, prog_var::out,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
set_of_progvar::in, set_of_progvar::out, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out) is det.
|
|
|
|
make_unification(Context, Var0, Var, !Unifications, !SeenSoFar, !VarSet,
|
|
!VarTypes) :-
|
|
( if set_of_var.contains(!.SeenSoFar, Var0) then
|
|
% Make new variable.
|
|
OldVarName = varset.lookup_name(!.VarSet, Var0),
|
|
lookup_var_type(!.VarTypes, Var0, OldVarType),
|
|
NewVarName = "Arg_" ++ OldVarName,
|
|
varset.new_uniquely_named_var(NewVarName, Var, !VarSet),
|
|
add_var_type(Var, OldVarType, !VarTypes),
|
|
|
|
% Make new unification.
|
|
create_atomic_complicated_unification(Var0, rhs_var(Var), Context,
|
|
umc_implicit("Making call args unique for cbma"), [], purity_pure,
|
|
UnificationGoal0),
|
|
UnificationGoal0 =
|
|
hlds_goal(UnificationGoalExpr, UnificationGoalInfo0),
|
|
goal_info_set_nonlocals(set_of_var.list_to_set([Var0, Var]),
|
|
UnificationGoalInfo0, UnificationGoalInfo),
|
|
UnificationGoal =
|
|
hlds_goal(UnificationGoalExpr, UnificationGoalInfo),
|
|
!:Unifications = [UnificationGoal | !.Unifications]
|
|
else
|
|
Var = Var0
|
|
),
|
|
set_of_var.insert(Var, !SeenSoFar).
|
|
|
|
% replace_call_with_conjunction(NewCallGoalExpr, Unifications, NewArgs,
|
|
% GoalExpr, !GoalInfo):
|
|
%
|
|
% Makes a conjunction out of CallGoalExpr and Unifications - the
|
|
% conjunction becomes GoalExpr and the goal info for the conjunction
|
|
% becomes !:GoalInfo. Goal info for CallGoalExpr and GoalExpr is
|
|
% created with the assumption that GoalExpr replaces a call and that
|
|
% CallGoalExpr is that call with its arguments replaced by NewArgs
|
|
% (where Unifications contains unfications between old arguments
|
|
% and their new replacements).
|
|
%
|
|
:- pred replace_call_with_conjunction(hlds_goal_expr::in, list(hlds_goal)::in,
|
|
list(prog_var)::in, hlds_goal_expr::out,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
replace_call_with_conjunction(CallGoalExpr, Unifications, NewArgs, GoalExpr,
|
|
!GoalInfo) :-
|
|
CallGoalInfo0 = !.GoalInfo,
|
|
Context = goal_info_get_context(CallGoalInfo0),
|
|
CallNonlocals0 = goal_info_get_nonlocals(CallGoalInfo0),
|
|
set_of_var.insert_list(NewArgs, CallNonlocals0, CallNonlocals),
|
|
goal_info_set_nonlocals(CallNonlocals, CallGoalInfo0, CallGoalInfo),
|
|
Goals = [hlds_goal(CallGoalExpr, CallGoalInfo) | Unifications],
|
|
|
|
% Create the new conjunction
|
|
GoalExpr = conj(plain_conj, Goals),
|
|
goal_info_init(!:GoalInfo),
|
|
goal_info_set_context(Context, !GoalInfo),
|
|
goal_info_set_nonlocals(CallNonlocals0, !GoalInfo).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
module_info_pred_status_is_imported(ModuleInfo, PredId) :-
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
|
|
% The following used because pred_info_is_imported/2 is not
|
|
% as comprehensive as status_is_imported/2.
|
|
pred_info_get_status(PredInfo, Status),
|
|
pred_status_is_imported(Status) = yes.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Print the constraints to the current output stream in human readable
|
|
% format. It titles each pred's constraints with a module qualification
|
|
% based on the default filename for the module followed by the
|
|
% predicate's name.
|
|
%
|
|
pretty_print_pred_constraints_map(OutputStream, ModuleInfo, ConstraintVarSet,
|
|
PredConstraintsMap, !IO) :-
|
|
ConstrainedPreds = map.keys(PredConstraintsMap),
|
|
list.foldl(
|
|
pretty_print_pred_constraints(OutputStream, ModuleInfo,
|
|
ConstraintVarSet, PredConstraintsMap),
|
|
ConstrainedPreds, !IO).
|
|
|
|
% Print the constraints for the specified predicate from the
|
|
% pred_constraints_map to the current output stream in a human
|
|
% readable format.
|
|
%
|
|
:- pred pretty_print_pred_constraints(io.text_output_stream::in,
|
|
module_info::in, mc_varset::in, pred_constraints_map::in, pred_id::in,
|
|
io::di, io::uo) is det.
|
|
|
|
pretty_print_pred_constraints(OutputStream, ModuleInfo, ConstraintVarSet,
|
|
PredConstraintsMap, PredId, !IO) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
|
|
% Start with a blank line.
|
|
write_error_pieces_plain(OutputStream, Globals, [fixed("")], !IO),
|
|
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
write_error_pieces_plain(OutputStream, Globals,
|
|
[words("Constraints for")] ++
|
|
describe_one_pred_info_name(no, should_module_qualify, [], PredInfo) ++
|
|
[suffix(":")], !IO),
|
|
|
|
map.lookup(PredConstraintsMap, PredId, {_, PredConstraints}),
|
|
AllProcAnnConstraints = allproc_annotated_constraints(PredConstraints),
|
|
dump_constraints_and_annotations(OutputStream, Globals, ConstraintVarSet,
|
|
AllProcAnnConstraints, !IO),
|
|
list.foldl(
|
|
pretty_print_proc_constraints(OutputStream, ModuleInfo,
|
|
ConstraintVarSet, PredConstraints, PredId),
|
|
pred_info_all_procids(PredInfo), !IO).
|
|
|
|
% Puts the constraints specific to the procedure indicated from
|
|
% the pred_p_c_constraints to the current output stream in human
|
|
% readable format.
|
|
%
|
|
:- pred pretty_print_proc_constraints(io.text_output_stream::in,
|
|
module_info::in, mc_varset::in, pred_p_c_constraints::in,
|
|
pred_id::in, proc_id::in, io::di, io::uo) is det.
|
|
|
|
pretty_print_proc_constraints(OutputStream, ModuleInfo, ConstraintVarSet,
|
|
PredConstraints, PredId, ProcId, !IO) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
|
|
% Start with a blank line.
|
|
write_error_pieces_plain(OutputStream, Globals, [fixed("")], !IO),
|
|
|
|
PredProcId = proc(PredId, ProcId),
|
|
ProcNamePieces = describe_one_proc_name(ModuleInfo, no,
|
|
should_module_qualify, PredProcId),
|
|
write_error_pieces_plain(OutputStream, Globals,
|
|
ProcNamePieces ++ [suffix(":")], !IO),
|
|
ProcSpecAnnConstraints =
|
|
proc_specific_annotated_constraints(ProcId, PredConstraints),
|
|
dump_constraints_and_annotations(OutputStream, Globals, ConstraintVarSet,
|
|
ProcSpecAnnConstraints, !IO).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.prop_mode_constraints.
|
|
%----------------------------------------------------------------------------%
|