mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
The code in make_hlds_warn.m that is intended to generate singleton warnings
hasn't ever been able to handle code containing 'some [...]' goals properly.
The reason is that
- add_clause.m invokes make_hlds_warn.m only *after* it does quantification
on the body of the clause being added to the HLDS, but
- quantification has always replaced all lists of quantified variables
with the empty list.
This meant that
- we never could report code in which the only occurrence of a variable
was in a list of quantified variables, which is something we *should*
warn about, and
- we always did generate a singleton warning for code such as
"some [Val] map.search(Map, Key, Val)", which is something we *should not*
warn about.
This diff fixes this problem.
The main change is a mechanism that allows us to tell quantification.m
to keep lists of quantified variables intact. However, since the rest
of the compiler does not react well to these lists not being empty,
this diff
- gets make_hlds_warn.m to report whether the clause body goal, in which
quantification.m was told to preserve any lists of quantified variables,
*actually contained* any nonempty lists of quantified variables, and
- if it did, then we invoke quantification.m again, this time telling it
to nuke all lists of quantified variables.
This nuking has to be done relatively rarely, because only a very small
fraction of clauses contain any explicit quantification.
(An alternative design would be for make_hlds_warn.m to always nuke
any nonempty list of quantified variables it traversed. However, this would
require *always* rebuilding the clause body goal, which would probably
be slower on average.)
The above is the main change in this diff. However, the change that is
responsible for the bulk of the diff is the addition of a flag to
exist_quant scopes to specify whether that scope was created by the user
or by the compiler. This is needed because if make_hlds_warn.m sees code
such as "some [Val] map.search(Map, Key, Val)", it definitely *should*
generate a warning about Val being singleton (if it does not occur outside
this code) if the "some [Val]" scope was put there by the compiler.
compiler/make_hlds_warn.m:
Treat user-generated exist_quant scopes as before (the old code did
the right thing to generate warnings, it was just given wrong inputs).
Treat compiler-generated exist_quant scopes as if they weren't there,
for warning-generating purposes.
To make this distinction possible, use separate code to handle
exist_quant and promise_solutions scopes.
Record whether the goal traversal has seen any nonempty lists of quantified
variables, and return this info to the caller in add_clause.m.
Encode the nonempty nature of a list in the argument structure of a
predicate.
Update some obsolete terminology in variable and field names.
Clarify the logic of some code.
compiler/quantification.m:
Add the keep_quant/do_not_keep_quant switch described above.
Add some documentation of the predicates to which it is applicable.
Add free_goal_expr_vars, a version of free_goal_vars that takes
only a goal expr, without the goal info. At one point, I thought
this diff needed it. It does not, so the new function is not used,
but there is also not much point in deleting it, Simplify the code
of free_goal_vars, deleting one of its callees after inlining it
at its only call site.
Replace a appended-to-at-the-front-and-then-reversed list with a cord.
compiler/hlds_goal.m:
Add the created-by-user-or-compiler flag to exist_quant scopes.
compiler/add_clause.m:
Move the code that invokes make_hlds_warn.m to warn about singletons
into the clauses_info_add_clause predicate, whose subcontractor
add_clause_transform does the initial quantification. The reason
for this move is that we have never generated singleton variable warnings
for clauses that were read in from .opt files, or for clauses which are
known to have syntax errors. With the new setup, if we such clauses,
clauses_info_add_clause can, and does, tell add_clause_transform
to tell quantification.m to nuke lists of quantified variable
right away. It is only for the clauses we *can* warn about
that clauses_info_add_clause will tell add_clause_transform
to keep those variables, and will then itself invoke the code
in make_hlds_warn.m that warns about singletons, followed, if needed,
by a var-list-nuking reinvocation of quantification.
This centralization of the code relevant to warning code in
clauses_info_add_clause also allows the deletion of several of its
output arguments, since its two callers used those arguments
only to invoke the warning-generation code. It also eliminates
the duplication of code in those two callers.
compiler/instance_method_clauses.m:
Conform to the change in add_clause.m.
compiler/add_foreign_proc.m:
compiler/assertion.m:
compiler/constraint.m:
compiler/cse_detection.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/format_call.m:
compiler/goal_expr_to_goal.m:
compiler/goal_util.m:
compiler/hlds_desc.m:
compiler/hlds_out_goal.m:
compiler/interval.m:
compiler/lambda.m:
compiler/mark_tail_calls.m:
compiler/ml_code_gen.m:
compiler/mode_constraints.m:
compiler/modecheck_goal.m:
compiler/polymorphism_goal.m:
compiler/pre_quantification.m:
compiler/purity.m:
compiler/saved_vars.m:
compiler/simplify_goal_scope.m:
compiler/simplify_proc.m:
compiler/state_var.m:
compiler/stm_expand.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/try_expand.m:
compiler/typecheck.m:
compiler/unique_modes.m:
Conform to the change in hlds_goal.m and/or quantification.m.
compiler/options.m:
Add a way to detect the presence of this fix in the installed compiler.
tests/valid/Mmakefile:
Enable the old test case for this problem, some_singleton,
which we haven't passed until now.
tests/warnings/Mmakefile:
Enable the missing_singleton_warning test case, which we haven't passed
until now.
172 lines
7.3 KiB
Mathematica
172 lines
7.3 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2022 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% This module produces definitions for the predicates that implement
|
|
% instance methods.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module hlds.make_hlds.instance_method_clauses.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.make_hlds.qual_info.
|
|
:- import_module hlds.status.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
:- import_module term.
|
|
|
|
% Given the instance_proc_def for a predicate or function from an instance
|
|
% declaration, produce the clauses_info for that instance_proc_def.
|
|
%
|
|
:- pred produce_instance_method_clauses(instance_proc_def::in,
|
|
pred_or_func::in, list(mer_type)::in, pred_markers::in, term.context::in,
|
|
instance_status::in, clauses_info::out, tvarset::in, tvarset::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.hlds_args.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.make_hlds.add_clause.
|
|
:- import_module hlds.make_hlds.state_var.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.maybe_error.
|
|
:- import_module parse_tree.prog_item.
|
|
:- import_module parse_tree.prog_util.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_table.
|
|
:- import_module parse_tree.vartypes.
|
|
|
|
:- import_module cord.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
produce_instance_method_clauses(InstanceProcDefn, PredOrFunc, ArgTypes,
|
|
Markers, Context, InstanceStatus, ClausesInfo,
|
|
!TVarSet, !ModuleInfo, !QualInfo, !Specs) :-
|
|
PredFormArity = arg_list_arity(ArgTypes),
|
|
(
|
|
% Handle the `pred(<MethodName>/<Arity>) is <ImplName>' syntax.
|
|
InstanceProcDefn = instance_proc_def_name(InstancePredName),
|
|
% Add the body of the introduced pred.
|
|
% First the goal info, ...
|
|
PredFormArity = pred_form_arity(PredFormArityInt),
|
|
varset.init(VarSet0),
|
|
make_n_fresh_vars("HeadVar__", PredFormArityInt, HeadVars,
|
|
VarSet0, VarSet),
|
|
set_of_var.list_to_set(HeadVars, NonLocals),
|
|
( if check_marker(Markers, marker_is_impure) then
|
|
Purity = purity_impure
|
|
else if check_marker(Markers, marker_is_semipure) then
|
|
Purity = purity_semipure
|
|
else
|
|
Purity = purity_pure
|
|
),
|
|
instmap_delta_init_unreachable(DummyInstMapDelta),
|
|
DummyDetism = detism_erroneous,
|
|
goal_info_init(NonLocals, DummyInstMapDelta, DummyDetism, Purity,
|
|
Context, GoalInfo),
|
|
% ... and then the goal itself.
|
|
construct_and_record_pred_or_func_call(invalid_pred_id, PredOrFunc,
|
|
InstancePredName, HeadVars, GoalInfo, IntroducedGoal, !QualInfo),
|
|
IntroducedClause = clause(all_modes, IntroducedGoal, impl_lang_mercury,
|
|
Context, []),
|
|
|
|
vartypes_from_corresponding_lists(HeadVars, ArgTypes,
|
|
ExplicitVarTypes),
|
|
init_var_table(VarTable),
|
|
rtti_varmaps_init(RttiVarMaps),
|
|
map.init(TVarNameMap),
|
|
HeadVarVec = proc_arg_vector_init(PredOrFunc, HeadVars),
|
|
set_clause_list([IntroducedClause], ClausesRep),
|
|
ClausesInfo = clauses_info(VarSet, ExplicitVarTypes,
|
|
VarTable, RttiVarMaps, TVarNameMap, HeadVarVec, ClausesRep,
|
|
init_clause_item_numbers_comp_gen,
|
|
no_foreign_lang_clauses, no_clause_syntax_errors)
|
|
;
|
|
% Handle the arbitrary clauses syntax.
|
|
InstanceProcDefn = instance_proc_def_clauses(InstanceClausesCord),
|
|
InstanceClauses = cord.list(InstanceClausesCord),
|
|
% XXX CIT_TYPES: should be cit_types(ArgTypes)
|
|
clauses_info_init(PredOrFunc, cit_no_types(PredFormArity),
|
|
init_clause_item_numbers_comp_gen, ClausesInfo0),
|
|
list.foldl5(
|
|
produce_instance_method_clause(PredOrFunc, Context,
|
|
InstanceStatus),
|
|
InstanceClauses, !TVarSet, !ModuleInfo, !QualInfo,
|
|
ClausesInfo0, ClausesInfo, !Specs)
|
|
).
|
|
|
|
:- pred produce_instance_method_clause(pred_or_func::in,
|
|
prog_context::in, instance_status::in, item_clause_info::in,
|
|
tvarset::in, tvarset::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out, clauses_info::in, clauses_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
produce_instance_method_clause(PredOrFunc, Context, InstanceStatus,
|
|
InstanceClause, TVarSet0, TVarSet, !ModuleInfo, !QualInfo,
|
|
!ClausesInfo, !Specs) :-
|
|
InstanceClause = item_clause_info(ClausePredOrFunc, PredSymName,
|
|
HeadTerms0, ClauseVarSet, MaybeBodyGoal, _ClauseContext, _SeqNum),
|
|
% XXX Can this ever fail? If yes, we should generate an error message
|
|
% instead of aborting.
|
|
expect(unify(PredOrFunc, ClausePredOrFunc), $pred, "PredOrFunc mismatch"),
|
|
( if
|
|
illegal_state_var_func_result(PredOrFunc, HeadTerms0, StateVar,
|
|
StateVarContext)
|
|
then
|
|
TVarSet = TVarSet0,
|
|
report_illegal_func_svar_result(StateVarContext, ClauseVarSet,
|
|
StateVar, !Specs),
|
|
!:Specs = get_any_errors_warnings2(MaybeBodyGoal) ++ !.Specs
|
|
else
|
|
(
|
|
MaybeBodyGoal = error2(BodyGoalSpecs),
|
|
TVarSet = TVarSet0,
|
|
!:Specs = BodyGoalSpecs ++ !.Specs
|
|
;
|
|
MaybeBodyGoal = ok2(BodyGoal, BodyGoalWarningSpecs),
|
|
!:Specs = BodyGoalWarningSpecs ++ !.Specs,
|
|
expand_bang_state_pairs_in_terms(HeadTerms0, HeadTerms),
|
|
% AllProcIds is only used when the predicate has foreign procs,
|
|
% which the instance method pred should not have, so this
|
|
% dummy value should be ok.
|
|
AllProcIds = [],
|
|
% XXX STATUS
|
|
InstanceStatus = instance_status(OldImportStatus),
|
|
PredStatus = pred_status(OldImportStatus),
|
|
clauses_info_add_clause(all_modes, AllProcIds, PredStatus,
|
|
clause_not_for_promise, PredOrFunc, PredSymName, HeadTerms,
|
|
Context, item_no_seq_num, BodyGoal, ClauseVarSet,
|
|
TVarSet0, TVarSet, !ClausesInfo, !ModuleInfo,
|
|
!QualInfo, !Specs)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module hlds.make_hlds.instance_method_clauses.
|
|
%-----------------------------------------------------------------------------%
|