mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
compiler/options.m:
Move --warn-simple-code to the "warnings about dodgy code"
category, as
- most of the issues that it warns about are indeed evidence
of dodgy code, and
- its existing documentation also states that it is intended
to report likely-to-be-incorrect code.
Change the internal name of --warn-simple-code, in order to force
all its existing uses to be reclassified.
Let the --warn-redundant-code part stay in the style issues category,
since the code will work just the same if the redundancies are removed.
NEWS.md:
Announce the new option.
compiler/simplify_goal_call.m:
Reclassify all uses of --warn-simple-code.
Add a missing condition in an if-then-else.
compiler/add_clause.m:
compiler/add_type.m:
compiler/check_type_inst_mode_defns.m:
compiler/convert_import_use.m:
compiler/convert_parse_tree.m:
compiler/det_analysis.m:
compiler/det_infer_goal.m:
compiler/mercury_compile_front_end.m:
compiler/mode_errors.m:
compiler/simplify_goal.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_info.m:
compiler/simplify_tasks.m:
compiler/state_var.m:
Reclassify all uses of --warn-simple-code.
tests/warnings/help_text.err_exp:
Expect the updated help text.
610 lines
23 KiB
Mathematica
610 lines
23 KiB
Mathematica
%----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%----------------------------------------------------------------------------%
|
|
% Copyright (C) 2014-2018, 2020-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: simplify_goal.m.
|
|
%
|
|
% This module handles simplifications that apply to all goals, and then
|
|
% distributes the goal-type-specific work to other submodules of simplify.m.
|
|
%
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.simplify.simplify_goal.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.simplify.common.
|
|
:- import_module check_hlds.simplify.simplify_info.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.instmap.
|
|
|
|
% Handle simplification of all goals.
|
|
%
|
|
% This involves trying to simplify the interaction between the given goal
|
|
% and its surrounding goals before invoking the goal-type-specific
|
|
% simplification code on it.
|
|
%
|
|
% simplify_goal and its goal-type-specific subcontractor predicates
|
|
% pass around information about the context surrounding the goal
|
|
% currently being analyzed in the simplify_nested_context argument.
|
|
% They pass around information about the program point just before
|
|
% the current goal in the instmap and common_info arguments.
|
|
% They pass around information that is global to the simplification
|
|
% process as a whole in the simplify_info arguments.
|
|
%
|
|
% These predicates return the common_info appropriate to the program
|
|
% point after the goal that was just analyzed in the common_info output
|
|
% argument. They do not return the new instmap, because it is not
|
|
% necessary. The two predicates that need to know the instmap
|
|
% after the goal that was just analyzed (simplify_goal_conj, after
|
|
% a conjunct, and simplify_goal_ite, after the condition)
|
|
% apply the goal's instmap delta themselves.
|
|
%
|
|
:- pred simplify_goal(hlds_goal::in, hlds_goal::out,
|
|
simplify_nested_context::in, instmap::in,
|
|
common_info::in, common_info::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
% Invoke the goal-type-specific simplification code on the given goal.
|
|
%
|
|
:- pred simplify_goal_expr(hlds_goal_expr::in, hlds_goal_expr::out,
|
|
hlds_goal_info::in, hlds_goal_info::out,
|
|
simplify_nested_context::in, instmap::in,
|
|
common_info::in, common_info::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%
|
|
% Utility predicates needed by the simplifications of several kinds of goals.
|
|
%
|
|
|
|
% When removing a level of wrapping around a goal, if the determinisms
|
|
% are not the same, we really need to rerun determinism analysis on the
|
|
% procedure. I think this is a similar situation to inlining of erroneous
|
|
% goals. The safe thing to do is to wrap a `scope' around the inner goal
|
|
% if the inner and outer determinisms are not the same. It probably
|
|
% won't happen that often.
|
|
%
|
|
:- pred simplify_maybe_wrap_goal(hlds_goal_info::in, hlds_goal_info::in,
|
|
hlds_goal_expr::in, hlds_goal_expr::out, hlds_goal_info::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.simplify.simplify_goal_call.
|
|
:- import_module check_hlds.simplify.simplify_goal_conj.
|
|
:- import_module check_hlds.simplify.simplify_goal_disj.
|
|
:- import_module check_hlds.simplify.simplify_goal_ite.
|
|
:- import_module check_hlds.simplify.simplify_goal_scope.
|
|
:- import_module check_hlds.simplify.simplify_goal_switch.
|
|
:- import_module check_hlds.simplify.simplify_goal_unify.
|
|
:- import_module hlds.goal_contains.
|
|
:- import_module hlds.goal_form.
|
|
:- import_module hlds.hlds_markers.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_goal.
|
|
:- import_module hlds.make_goal.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_db.
|
|
:- import_module transform_hlds.
|
|
:- import_module transform_hlds.pd_cost.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
simplify_goal(Goal0, Goal, NestedContext0, InstMap0, !Common, !Info) :-
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
FeaturesSet0 = goal_info_get_features(GoalInfo0),
|
|
( if set.contains(FeaturesSet0, feature_duplicated_for_switch) then
|
|
NestedContext = NestedContext0 ^ snc_inside_dupl_for_switch := yes
|
|
else
|
|
NestedContext = NestedContext0
|
|
),
|
|
( if set.contains(FeaturesSet0, feature_contains_trace) then
|
|
simplify_info_set_found_contains_trace(yes, !Info),
|
|
Goal0ContainsTrace = contains_trace_goal
|
|
else
|
|
Goal0ContainsTrace = contains_no_trace_goal
|
|
),
|
|
|
|
maybe_simplify_goal_to_true_or_fail(InstMap0, Goal0ContainsTrace,
|
|
Goal0, Goal1, !Info),
|
|
|
|
% Remove unnecessary explicit quantifications before working out
|
|
% whether the goal can cause a stack flush.
|
|
Goal1 = hlds_goal(GoalExpr1, GoalInfo1),
|
|
( if GoalExpr1 = scope(Reason1, SomeGoal1) then
|
|
try_to_merge_nested_scopes(Reason1, SomeGoal1, GoalInfo1, Goal2)
|
|
else
|
|
Goal2 = Goal1
|
|
),
|
|
( if
|
|
simplify_do_elim_removable_scopes(!.Info),
|
|
Goal2 = hlds_goal(scope(Reason2, SomeGoal2), _GoalInfo2),
|
|
(
|
|
Reason2 = barrier(removable)
|
|
;
|
|
Reason2 = from_ground_term(_, Kind),
|
|
Kind = from_ground_term_other
|
|
)
|
|
then
|
|
Goal3 = SomeGoal2
|
|
else
|
|
Goal3 = Goal2
|
|
),
|
|
Goal3 = hlds_goal(GoalExpr3, GoalInfo3),
|
|
|
|
maybe_handle_stack_flush(before, GoalExpr3, !Common),
|
|
simplify_goal_expr(GoalExpr3, GoalExpr4, GoalInfo3, GoalInfo4,
|
|
NestedContext, InstMap0, !Common, !Info),
|
|
maybe_handle_stack_flush(after, GoalExpr4, !Common),
|
|
|
|
enforce_unreachability_invariant(GoalInfo4, GoalInfo, !Info),
|
|
trace [compile_time(flag("simplify_merge_switch")), io(!IO)] (
|
|
% If you want to debug one specific goal's transformation, then
|
|
% - add a goal feature to the goal at the point at which you decide
|
|
% you want to debug it, and then
|
|
% - replace the semidet_fail here with a test for that feature.
|
|
( if semidet_fail then
|
|
io.stderr_stream(StdErr, !IO),
|
|
simplify_info_get_module_info(!.Info, TraceModuleInfo),
|
|
simplify_info_get_var_table(!.Info, TraceVarTable),
|
|
simplify_info_get_tvarset(!.Info, TVarSet),
|
|
simplify_info_get_inst_varset(!.Info, InstVarSet),
|
|
TraceVarNameSrc = vns_var_table(TraceVarTable),
|
|
|
|
Goal5 = hlds_goal(GoalExpr4, GoalInfo),
|
|
io.write_string(StdErr, "\nMerge goal before\n\n", !IO),
|
|
dump_goal_nl(StdErr, TraceModuleInfo, TraceVarNameSrc,
|
|
TVarSet, InstVarSet, Goal0, !IO),
|
|
io.write_string(StdErr, "\nMerge goal after\n\n", !IO),
|
|
dump_goal_nl(StdErr, TraceModuleInfo, TraceVarNameSrc,
|
|
TVarSet, InstVarSet, Goal5, !IO)
|
|
else
|
|
true
|
|
)
|
|
),
|
|
Goal = hlds_goal(GoalExpr4, GoalInfo).
|
|
|
|
:- pred maybe_simplify_goal_to_true_or_fail(instmap::in,
|
|
contains_trace_goal::in, hlds_goal::in, hlds_goal::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
maybe_simplify_goal_to_true_or_fail(InstMap0, Goal0ContainsTrace,
|
|
Goal0, Goal, !Info) :-
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
some [!ModuleInfo] (
|
|
simplify_info_get_module_info(!.Info, !:ModuleInfo),
|
|
goal_can_loop_or_throw_imaf(Goal0, Goal0CanLoopOrThrow, !ModuleInfo),
|
|
simplify_info_set_module_info(!.ModuleInfo, !Info)
|
|
),
|
|
Detism = goal_info_get_determinism(GoalInfo0),
|
|
Purity = goal_info_get_purity(GoalInfo0),
|
|
( if
|
|
% If --no-fully-strict, replace goals with determinism failure
|
|
% with `fail'.
|
|
|
|
Detism = detism_failure,
|
|
( Purity = purity_pure
|
|
; Purity = purity_semipure
|
|
),
|
|
Goal0ContainsTrace = contains_no_trace_goal,
|
|
( simplify_info_get_fully_strict(!.Info, not_fully_strict)
|
|
; Goal0CanLoopOrThrow = cannot_loop_or_throw
|
|
)
|
|
then
|
|
% Warn about this replacement, unless the goal either is, or contains,
|
|
% - an explicit `fail', or
|
|
% - a call to `builtin.false/0'.
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
( if
|
|
simplify_do_warn_dodgy_simple_code(!.Info),
|
|
not (
|
|
some [SubGoal] (
|
|
goal_contains_goal(Goal0, SubGoal),
|
|
( SubGoal = hlds_goal(disj([]), _)
|
|
; goal_is_call_to_builtin_false(SubGoal)
|
|
)
|
|
)
|
|
)
|
|
then
|
|
MainPieces = [words("Warning:")] ++
|
|
color_as_incorrect([words("this goal cannot succeed.")]) ++
|
|
[nl],
|
|
VerbosePieces =
|
|
[words("The compiler will optimize away this goal,"),
|
|
words("replacing it with"), quote("fail"), suffix("."), nl],
|
|
Msg = simple_msg(Context,
|
|
[always(MainPieces),
|
|
verbose_only(verbose_always, VerbosePieces)]),
|
|
Spec = error_spec($pred, severity_warning(warn_dodgy_simple_code),
|
|
phase_simplify(report_only_if_in_all_modes), [Msg]),
|
|
simplify_info_add_message(Spec, !Info)
|
|
else
|
|
true
|
|
),
|
|
|
|
% If the goal had any non-locals, we should requantify.
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
( if set_of_var.is_empty(NonLocals0) then
|
|
true
|
|
else
|
|
simplify_info_set_rerun_quant_instmap_delta(!Info)
|
|
),
|
|
goal_cost(Goal0, CostDelta),
|
|
simplify_info_incr_cost_delta(CostDelta, !Info),
|
|
Goal = fail_goal_with_context(Context)
|
|
else if
|
|
% If --no-fully-strict, replace goals which cannot fail and have
|
|
% no output variables with `true'. However, we don't do this for
|
|
% erroneous goals, since these may occur in conjunctions where there
|
|
% are no producers for some variables, and the code generator would
|
|
% fail for these.
|
|
|
|
determinism_components(Detism, cannot_fail, MaxSoln),
|
|
MaxSoln \= at_most_zero,
|
|
InstMapDelta = goal_info_get_instmap_delta(GoalInfo0),
|
|
NonLocalVars = goal_info_get_nonlocals(GoalInfo0),
|
|
some [ModuleInfo] (
|
|
simplify_info_get_module_info(!.Info, ModuleInfo),
|
|
simplify_info_get_var_table(!.Info, VarTable),
|
|
instmap_delta_no_output_vars(ModuleInfo, VarTable,
|
|
InstMap0, InstMapDelta, NonLocalVars)
|
|
),
|
|
( Purity = purity_pure
|
|
; Purity = purity_semipure
|
|
),
|
|
Goal0ContainsTrace = contains_no_trace_goal,
|
|
( simplify_info_get_fully_strict(!.Info, not_fully_strict)
|
|
; Goal0CanLoopOrThrow = cannot_loop_or_throw
|
|
)
|
|
then
|
|
% The following warning is disabled, because it often results in spurious
|
|
% warnings. Sometimes predicate calls are used just to constrain the types,
|
|
% to avoid type ambiguities or unbound type variables, and in such cases,
|
|
% it is perfectly legitimate for a call to be det and to have no outputs.
|
|
% There is no simple way of telling those cases from cases for which we
|
|
% really ought to warn.
|
|
% XXX This hasn't really been true since we added `with_type`.
|
|
%
|
|
% % warn about this, if the goal wasn't `true', wasn't `!',
|
|
% % and wasn't a deconstruction unification.
|
|
% % We don't warn about deconstruction unifications
|
|
% % with no outputs that always succeed, because that
|
|
% % would result in bogus warnings, since switch detection
|
|
% % converts deconstruction unifications that can fail
|
|
% % into ones that always succeed by moving the test into
|
|
% % the switch.
|
|
% % We also don't warn about conjunctions or existential
|
|
% % quantifications, because it seems that warnings in those
|
|
% % cases are usually spurious.
|
|
% ( if
|
|
% simplify_do_warn_simple_code(!.Info),
|
|
% % Goal0 \= conj(plain_conj, []) - _,
|
|
% not (Goal0 = call(_, _, _, _, _, SymName) - _,
|
|
% unqualify_name(SymName, "!")),
|
|
% Goal0 \= conj(plain_conj, _) - _,
|
|
% Goal0 \= some(_, _) - _,
|
|
% not (Goal0 = unify(_, _, _, Unification, _) - _,
|
|
% Unification = deconstruct(_, _, _, _, _))
|
|
% then
|
|
% Msg = det_goal_has_no_outputs,
|
|
% ContextMsg = context_det_msg(Context, Msg),
|
|
% simplify_info_add_simple_code_spec(ContextMsg, !Info)
|
|
% else
|
|
% true
|
|
% ),
|
|
|
|
% If the goal had any non-locals, we should requantify.
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
( if set_of_var.is_empty(NonLocals0) then
|
|
true
|
|
else
|
|
simplify_info_set_rerun_quant_instmap_delta(!Info)
|
|
),
|
|
goal_cost(Goal0, CostDelta),
|
|
simplify_info_incr_cost_delta(CostDelta, !Info),
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
Goal = true_goal_with_context(Context)
|
|
else
|
|
Goal = Goal0
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred goal_is_call_to_builtin_false(hlds_goal::in) is semidet.
|
|
|
|
goal_is_call_to_builtin_false(hlds_goal(GoalExpr, _)) :-
|
|
GoalExpr = plain_call(_, _, _, _, _, SymName),
|
|
SymName = qualified(mercury_public_builtin_module, "false").
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
simplify_goal_expr(!GoalExpr, !GoalInfo, NestedContext0,
|
|
InstMap0, !Common, !Info) :-
|
|
(
|
|
!.GoalExpr = conj(ConjType, Goals),
|
|
(
|
|
ConjType = plain_conj,
|
|
simplify_goal_plain_conj(Goals, !:GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
ConjType = parallel_conj,
|
|
simplify_goal_parallel_conj(Goals, !:GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
)
|
|
;
|
|
!.GoalExpr = disj(_),
|
|
simplify_goal_disj(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = switch(_, _, _),
|
|
simplify_goal_switch(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = if_then_else(_, _, _, _),
|
|
simplify_goal_ite(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = negation(_),
|
|
simplify_goal_neg(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = scope(_, _),
|
|
simplify_goal_scope(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = unify(_, _, _, _, _),
|
|
simplify_goal_unify(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = plain_call(_, _, _, _, _, _),
|
|
simplify_goal_plain_call(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = generic_call(_, _, _, _, _),
|
|
simplify_goal_generic_call(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = call_foreign_proc(_, _, _, _, _, _, _),
|
|
simplify_goal_foreign_proc(!GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
!.GoalExpr = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners),
|
|
simplify_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_c
|
|
;
|
|
( Target = target_csharp, TargetStr = "C#"
|
|
; Target = target_java, TargetStr = "Java"
|
|
),
|
|
Phase = phase_simplify(report_in_any_mode),
|
|
StmPieces = [words("Error: atomic goals are"),
|
|
words("not implemented yet when targeting"),
|
|
words(TargetStr), suffix("."), nl],
|
|
StmSpec = spec($pred, severity_error, Phase,
|
|
goal_info_get_context(!.GoalInfo), StmPieces),
|
|
simplify_info_add_message(StmSpec, !Info)
|
|
),
|
|
simplify_goal_atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners,
|
|
!:GoalExpr, !GoalInfo,
|
|
NestedContext0, InstMap0, !Common, !Info)
|
|
;
|
|
ShortHand0 = try_goal(_, _, _),
|
|
% These should have been expanded out by now.
|
|
unexpected($pred, "try_goal")
|
|
;
|
|
ShortHand0 = bi_implication(_, _),
|
|
% These should have been expanded out by now.
|
|
unexpected($pred, "bi_implication")
|
|
)
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- type before_after
|
|
---> before
|
|
; after.
|
|
|
|
:- pred maybe_handle_stack_flush(before_after::in, hlds_goal_expr::in,
|
|
common_info::in, common_info::out) is det.
|
|
|
|
maybe_handle_stack_flush(BeforeAfter, GoalExpr, !Common) :-
|
|
WillFlush = will_flush(BeforeAfter, GoalExpr),
|
|
(
|
|
WillFlush = yes,
|
|
common_info_stack_flush(!Common)
|
|
;
|
|
WillFlush = no
|
|
).
|
|
|
|
% Return `no' if execution of the given goal cannot encounter a context
|
|
% that causes any variable to be flushed to its stack slot or to a
|
|
% register at the specified time, and `yes' otherwise.
|
|
%
|
|
:- func will_flush(before_after, hlds_goal_expr) = bool.
|
|
|
|
will_flush(BeforeAfter, GoalExpr) = WillFlush :-
|
|
(
|
|
GoalExpr = unify(_, _, _, Unify, _),
|
|
( if Unify = complicated_unify(_, _, _) then
|
|
WillFlush = yes
|
|
else
|
|
WillFlush = no
|
|
)
|
|
;
|
|
GoalExpr = plain_call(_, _, _, BuiltinState, _, _),
|
|
(
|
|
BuiltinState = inline_builtin,
|
|
WillFlush = no
|
|
;
|
|
BuiltinState = not_builtin,
|
|
(
|
|
BeforeAfter = before,
|
|
WillFlush = no
|
|
;
|
|
BeforeAfter = after,
|
|
WillFlush = yes
|
|
)
|
|
)
|
|
;
|
|
GoalExpr = generic_call(GenericCall, _, _, _, _),
|
|
(
|
|
BeforeAfter = before,
|
|
WillFlush = no
|
|
;
|
|
BeforeAfter = after,
|
|
(
|
|
( GenericCall = higher_order(_, _, _, _, _)
|
|
; GenericCall = class_method(_, _, _, _)
|
|
),
|
|
WillFlush = yes
|
|
;
|
|
( GenericCall = event_call(_)
|
|
; GenericCall = cast(_)
|
|
),
|
|
WillFlush = no
|
|
)
|
|
)
|
|
;
|
|
GoalExpr = call_foreign_proc(_, _, _, _, _, _, _),
|
|
(
|
|
BeforeAfter = before,
|
|
WillFlush = no
|
|
;
|
|
BeforeAfter = after,
|
|
WillFlush = yes
|
|
)
|
|
;
|
|
GoalExpr = conj(ConjType, _),
|
|
(
|
|
ConjType = plain_conj,
|
|
WillFlush = no
|
|
;
|
|
ConjType = parallel_conj,
|
|
WillFlush = yes
|
|
)
|
|
;
|
|
GoalExpr = switch(_, _, _),
|
|
WillFlush = no
|
|
;
|
|
GoalExpr = disj(_),
|
|
(
|
|
BeforeAfter = before,
|
|
WillFlush = yes
|
|
;
|
|
BeforeAfter = after,
|
|
WillFlush = no
|
|
)
|
|
;
|
|
GoalExpr = if_then_else(_, _, _, _),
|
|
(
|
|
BeforeAfter = before,
|
|
WillFlush = yes
|
|
;
|
|
BeforeAfter = after,
|
|
WillFlush = no
|
|
)
|
|
;
|
|
GoalExpr = negation(_),
|
|
WillFlush = yes
|
|
;
|
|
GoalExpr = scope(_, _),
|
|
WillFlush = no
|
|
;
|
|
GoalExpr = shorthand(ShortHand),
|
|
(
|
|
ShortHand = atomic_goal(_, _, _, _, _MainGoal, _OrElseGoals, _),
|
|
WillFlush = yes
|
|
;
|
|
ShortHand = try_goal(_, _, _),
|
|
WillFlush = yes
|
|
;
|
|
ShortHand = bi_implication(_, _),
|
|
% These should have been expanded out by now.
|
|
unexpected($pred, "bi_implication")
|
|
)
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Ensure that the mode information and the determinism information
|
|
% say consistent things about unreachability.
|
|
%
|
|
:- pred enforce_unreachability_invariant(
|
|
hlds_goal_info::in, hlds_goal_info::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
enforce_unreachability_invariant(GoalInfo0, GoalInfo, !Info) :-
|
|
Determinism0 = goal_info_get_determinism(GoalInfo0),
|
|
InstmapDelta0 = goal_info_get_instmap_delta(GoalInfo0),
|
|
determinism_components(Determinism0, CanFail0, NumSolns0),
|
|
( if
|
|
NumSolns0 = at_most_zero,
|
|
instmap_delta_is_reachable(InstmapDelta0)
|
|
then
|
|
instmap_delta_init_unreachable(UnreachableInstMapDelta),
|
|
goal_info_set_instmap_delta(UnreachableInstMapDelta,
|
|
GoalInfo0, GoalInfo),
|
|
simplify_info_set_rerun_det(!Info)
|
|
else if
|
|
instmap_delta_is_unreachable(InstmapDelta0),
|
|
NumSolns0 \= at_most_zero
|
|
then
|
|
determinism_components(Determinism, CanFail0, at_most_zero),
|
|
goal_info_set_determinism(Determinism, GoalInfo0, GoalInfo),
|
|
simplify_info_set_rerun_det(!Info)
|
|
else
|
|
GoalInfo = GoalInfo0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
simplify_maybe_wrap_goal(OuterGoalInfo, InnerGoalInfo, GoalExpr1,
|
|
GoalExpr, GoalInfo, !Info) :-
|
|
InnerDet = goal_info_get_determinism(InnerGoalInfo),
|
|
OuterDet = goal_info_get_determinism(OuterGoalInfo),
|
|
( if InnerDet = OuterDet then
|
|
GoalExpr = GoalExpr1,
|
|
GoalInfo = InnerGoalInfo
|
|
else
|
|
% XXX There are some inner goals for which it does not make sense
|
|
% to wrap a scope around it. This includes disj([]), which can be
|
|
% created by e.g. delete_tail_unreachable_goals.
|
|
GoalExpr = scope(commit(do_not_force_pruning),
|
|
hlds_goal(GoalExpr1, InnerGoalInfo)),
|
|
GoalInfo = OuterGoalInfo,
|
|
simplify_info_set_rerun_det(!Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.simplify.simplify_goal.
|
|
%---------------------------------------------------------------------------%
|