mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 13:23:53 +00:00
Estimated hours taken: 2.5 Branches: main Enhance the state variable notation to recognise !X ^ field_list := Term as a synonym for !:X = !.X ^ field_list := Term. NEWS: Announce the new construct. compiler/add_clause.m: Recognise this new construct, and transform it. doc/reference_manual.texi: Document the change. tests/hard_coded/Mmakefile: tests/hard_coded/sv_record_update.m: tests/hard_coded/sv_record_update.exp: A test case for this feature.
1263 lines
56 KiB
Mathematica
1263 lines
56 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1993-2007 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 hlds.make_hlds.add_clause.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.make_hlds.qual_info.
|
|
:- import_module hlds.make_hlds.state_var.
|
|
:- import_module hlds.quantification.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred module_add_clause(prog_varset::in, pred_or_func::in, sym_name::in,
|
|
list(prog_term)::in, goal::in, import_status::in, prog_context::in,
|
|
goal_type::in, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
:- pred clauses_info_add_clause(list(proc_id)::in,
|
|
prog_varset::in, tvarset::in, list(prog_term)::in, goal::in,
|
|
prog_context::in, import_status::in, pred_or_func::in, arity::in,
|
|
goal_type::in, hlds_goal::out, prog_varset::out, tvarset::out,
|
|
clauses_info::in, clauses_info::out, list(quant_warning)::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
% Convert goals from the prog_data `goal' structure into the HLDS
|
|
% `hlds_goal' structure. At the same time, convert it to super-homogeneous
|
|
% form by unravelling all the complex unifications, and annotate those
|
|
% unifications with a unify_context so that we can still give good error
|
|
% messages. And also at the same time, apply the given substitution to
|
|
% the goal, to rename it apart from the other clauses.
|
|
%
|
|
:- pred transform_goal(goal::in, prog_substitution::in, hlds_goal::out,
|
|
int::out, prog_varset::in, prog_varset::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.clause_to_proc.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_args.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_error_util.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.make_hlds.add_pragma.
|
|
:- import_module hlds.make_hlds.add_pred.
|
|
:- import_module hlds.make_hlds.field_access.
|
|
:- import_module hlds.make_hlds.make_hlds_warn.
|
|
:- import_module hlds.make_hlds.superhomogeneous.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.mercury_to_mercury.
|
|
:- import_module parse_tree.module_qual.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_io_util.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.prog_util.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
module_add_clause(ClauseVarSet, PredOrFunc, PredName, Args0, Body, Status,
|
|
Context, GoalType, !ModuleInfo, !QualInfo, !Specs) :-
|
|
( illegal_state_var_func_result(PredOrFunc, Args0, SVar) ->
|
|
IllegalSVarResult = yes(SVar)
|
|
;
|
|
IllegalSVarResult = no
|
|
),
|
|
ArityAdjustment = ( if IllegalSVarResult = yes(_) then -1 else 0 ),
|
|
Args = expand_bang_state_var_args(Args0),
|
|
|
|
% Lookup the pred declaration in the predicate table.
|
|
% (If it's not there, call maybe_undefined_pred_error and insert
|
|
% an implicit declaration for the predicate.)
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
list.length(Args, Arity0),
|
|
Arity = Arity0 + ArityAdjustment,
|
|
some [!PredInfo, !PredicateTable] (
|
|
module_info_get_predicate_table(!.ModuleInfo, !:PredicateTable),
|
|
(
|
|
predicate_table_search_pf_sym_arity(!.PredicateTable,
|
|
is_fully_qualified, PredOrFunc, PredName, Arity, [PredId0])
|
|
->
|
|
PredId = PredId0,
|
|
( GoalType = goal_type_promise(_) ->
|
|
NameString = sym_name_to_string(PredName),
|
|
string.format("%s %s %s (%s).\n",
|
|
[s("Attempted to introduce a predicate"),
|
|
s("for a promise with an identical"),
|
|
s("name to an existing predicate"),
|
|
s(NameString)], String),
|
|
unexpected(this_file, String)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
% A promise will not have a corresponding pred declaration.
|
|
(
|
|
GoalType = goal_type_promise(_)
|
|
->
|
|
HeadVars = term.term_list_to_var_list(Args),
|
|
preds_add_implicit_for_assertion(HeadVars, !.ModuleInfo,
|
|
ModuleName, PredName, Arity, Status, Context, PredOrFunc,
|
|
PredId, !PredicateTable),
|
|
module_info_set_predicate_table(!.PredicateTable, !ModuleInfo)
|
|
;
|
|
preds_add_implicit_report_error(ModuleName, PredOrFunc,
|
|
PredName, Arity, Status, no, Context,
|
|
origin_user(PredName), "clause", PredId, !ModuleInfo,
|
|
!Specs)
|
|
)
|
|
),
|
|
% Lookup the pred_info for this pred, add the clause to the
|
|
% clauses_info in the pred_info, if there are no modes add an
|
|
% `infer_modes' marker, and then save the pred_info.
|
|
module_info_get_predicate_table(!.ModuleInfo, !:PredicateTable),
|
|
predicate_table_get_preds(!.PredicateTable, Preds0),
|
|
map.lookup(Preds0, PredId, !:PredInfo),
|
|
|
|
trace [io(!IO)] (
|
|
globals.io_lookup_bool_option(very_verbose, VeryVerbose, !IO),
|
|
(
|
|
VeryVerbose = yes,
|
|
pred_info_get_clauses_info(!.PredInfo, MsgClauses),
|
|
NumClauses =
|
|
num_clauses_in_clauses_rep(MsgClauses ^ clauses_rep),
|
|
io.format("%% Processing clause %d for ", [i(NumClauses + 1)],
|
|
!IO),
|
|
write_pred_or_func(PredOrFunc, !IO),
|
|
io.write_string(" `", !IO),
|
|
list.length(Args, PredArity0),
|
|
PredArity = PredArity0 + ArityAdjustment,
|
|
adjust_func_arity(PredOrFunc, OrigArity, PredArity),
|
|
prog_out.write_sym_name_and_arity(PredName/OrigArity, !IO),
|
|
io.write_string("'...\n", !IO)
|
|
;
|
|
VeryVerbose = no
|
|
)
|
|
),
|
|
|
|
% Opt_imported preds are initially tagged as imported, and are tagged
|
|
% as opt_imported only if/when we see a clause for them.
|
|
( Status = status_opt_imported ->
|
|
pred_info_set_import_status(status_opt_imported, !PredInfo),
|
|
pred_info_get_markers(!.PredInfo, InitMarkers0),
|
|
add_marker(marker_calls_are_fully_qualified,
|
|
InitMarkers0, InitMarkers),
|
|
pred_info_set_markers(InitMarkers, !PredInfo)
|
|
;
|
|
true
|
|
),
|
|
(
|
|
IllegalSVarResult = yes(StateVar)
|
|
->
|
|
report_illegal_func_svar_result(Context, ClauseVarSet, StateVar,
|
|
!Specs)
|
|
;
|
|
% User-supplied clauses for field access functions are not allowed
|
|
% -- the clauses are always generated by the compiler.
|
|
%
|
|
PredOrFunc = pf_function,
|
|
adjust_func_arity(pf_function, FuncArity, Arity),
|
|
is_field_access_function_name(!.ModuleInfo, PredName, FuncArity,
|
|
_, _),
|
|
|
|
% Don't report errors for clauses for field access
|
|
% function clauses in `.opt' files.
|
|
Status \= status_opt_imported
|
|
->
|
|
CallId = simple_call_id(PredOrFunc, PredName, Arity),
|
|
MainPieces = [words("Error: clause for automatically generated"),
|
|
words("field access"), simple_call(CallId), suffix("."), nl],
|
|
VerbosePieces = [words("Clauses for field access functions"),
|
|
words("are automatically generated by the compiler."),
|
|
words("To supply your own definition for a field access"),
|
|
words("function, for example to check the input"),
|
|
words("to a field update, give the field"),
|
|
words("of the constructor a different name.")],
|
|
Msg = simple_msg(Context,
|
|
[always(MainPieces), verbose_only(VerbosePieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs]
|
|
;
|
|
pred_info_is_builtin(!.PredInfo)
|
|
->
|
|
% When bootstrapping a change that redefines a builtin as
|
|
% normal Mercury code, you may need to disable this action.
|
|
Msg = simple_msg(Context,
|
|
[always([words("Error: clause for builtin.")])]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs]
|
|
;
|
|
pred_info_get_clauses_info(!.PredInfo, Clauses0),
|
|
pred_info_get_typevarset(!.PredInfo, TVarSet0),
|
|
maybe_add_default_func_mode(!PredInfo, _),
|
|
select_applicable_modes(Args, ClauseVarSet, Status, Context,
|
|
PredId, !.PredInfo, ArgTerms, ProcIdsForThisClause,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
clauses_info_add_clause(ProcIdsForThisClause, ClauseVarSet,
|
|
TVarSet0, ArgTerms, Body, Context, Status, PredOrFunc, Arity,
|
|
GoalType, Goal, VarSet, TVarSet, Clauses0, Clauses, Warnings,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
pred_info_set_clauses_info(Clauses, !PredInfo),
|
|
( GoalType = goal_type_promise(PromiseType) ->
|
|
pred_info_set_goal_type(goal_type_promise(PromiseType),
|
|
!PredInfo)
|
|
;
|
|
pred_info_update_goal_type(goal_type_clause, !PredInfo)
|
|
),
|
|
pred_info_set_typevarset(TVarSet, !PredInfo),
|
|
pred_info_get_arg_types(!.PredInfo, _ArgTVarSet, ExistQVars,
|
|
ArgTypes),
|
|
pred_info_set_arg_types(TVarSet, ExistQVars, ArgTypes, !PredInfo),
|
|
|
|
%
|
|
% Check if there are still no modes for the predicate, and if so,
|
|
% set the `infer_modes' flag for that predicate.
|
|
%
|
|
ProcIds = pred_info_all_procids(!.PredInfo),
|
|
(
|
|
ProcIds = [],
|
|
pred_info_get_markers(!.PredInfo, EndMarkers0),
|
|
add_marker(marker_infer_modes, EndMarkers0, EndMarkers),
|
|
pred_info_set_markers(EndMarkers, !PredInfo)
|
|
;
|
|
ProcIds = [_ | _]
|
|
),
|
|
map.det_update(Preds0, PredId, !.PredInfo, Preds),
|
|
predicate_table_set_preds(Preds, !PredicateTable),
|
|
module_info_set_predicate_table(!.PredicateTable, !ModuleInfo),
|
|
( Status = status_opt_imported ->
|
|
true
|
|
;
|
|
% Warn about singleton variables.
|
|
SimpleCallId = simple_call_id(PredOrFunc, PredName, Arity),
|
|
warn_singletons(VarSet, SimpleCallId, !.ModuleInfo, Goal,
|
|
!Specs),
|
|
% Warn about variables with overlapping scopes.
|
|
warn_overlap(Warnings, VarSet, SimpleCallId, !Specs)
|
|
)
|
|
)
|
|
).
|
|
|
|
% Extract the mode annotations (if any) from the clause arguments,
|
|
% and determine which mode(s) this clause should apply to.
|
|
%
|
|
:- pred select_applicable_modes(list(prog_term)::in, prog_varset::in,
|
|
import_status::in, prog_context::in, pred_id::in, pred_info::in,
|
|
list(prog_term)::out, list(proc_id)::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
select_applicable_modes(Args0, VarSet, Status, Context, PredId, PredInfo,
|
|
Args, ProcIds, !ModuleInfo, !QualInfo, !Specs) :-
|
|
get_mode_annotations(Args0, Args, empty, ModeAnnotations),
|
|
(
|
|
ModeAnnotations = modes(ModeList0),
|
|
|
|
% The user specified some mode annotations on this clause.
|
|
% First module-qualify the mode annotations. The annotations
|
|
% on clauses from `.opt' files will already be fully module
|
|
% qualified.
|
|
%
|
|
( Status = status_opt_imported ->
|
|
ModeList = ModeList0
|
|
;
|
|
qual_info_get_mq_info(!.QualInfo, MQInfo0),
|
|
qualify_clause_mode_list(ModeList0, ModeList, Context,
|
|
MQInfo0, MQInfo, !Specs),
|
|
qual_info_set_mq_info(MQInfo, !QualInfo)
|
|
),
|
|
|
|
% Now find the procedure which matches these mode annotations.
|
|
pred_info_get_procedures(PredInfo, Procs),
|
|
map.to_assoc_list(Procs, ExistingProcs),
|
|
(
|
|
get_procedure_matching_declmodes_with_renaming(ExistingProcs,
|
|
ModeList, !.ModuleInfo, ProcId)
|
|
->
|
|
ProcIds = [ProcId]
|
|
;
|
|
undeclared_mode_error(ModeList, VarSet, PredId, PredInfo,
|
|
!.ModuleInfo, Context, !Specs),
|
|
% apply the clause to all modes
|
|
% XXX would it be better to apply it to none?
|
|
ProcIds = pred_info_all_procids(PredInfo)
|
|
)
|
|
;
|
|
ModeAnnotations = empty,
|
|
( pred_info_pragma_goal_type(PredInfo) ->
|
|
% We are only allowed to mix foreign procs and
|
|
% mode specific clauses, so make this clause
|
|
% mode specific but apply to all modes.
|
|
ProcIds = pred_info_all_procids(PredInfo)
|
|
;
|
|
% this means the clauses applies to all modes
|
|
ProcIds = []
|
|
)
|
|
;
|
|
ModeAnnotations = none,
|
|
( pred_info_pragma_goal_type(PredInfo) ->
|
|
% We are only allowed to mix foreign procs and
|
|
% mode specific clauses, so make this clause
|
|
% mode specific but apply to all modes.
|
|
ProcIds = pred_info_all_procids(PredInfo)
|
|
;
|
|
% this means the clauses applies to all modes
|
|
ProcIds = []
|
|
)
|
|
;
|
|
ModeAnnotations = mixed,
|
|
PredIdStr = pred_id_to_string(!.ModuleInfo, PredId),
|
|
Pieces = [words("In clause for"), fixed(PredIdStr), suffix(":"), nl,
|
|
words("syntax error: some but not all arguments"),
|
|
words("have mode annotations."), nl],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs],
|
|
|
|
% Apply the clause to all modes.
|
|
% XXX Would it be better to apply it to none?
|
|
ProcIds = pred_info_all_procids(PredInfo)
|
|
).
|
|
|
|
:- pred undeclared_mode_error(list(mer_mode)::in, prog_varset::in,
|
|
pred_id::in, pred_info::in, module_info::in, prog_context::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
undeclared_mode_error(ModeList, VarSet, PredId, PredInfo, ModuleInfo, Context,
|
|
!Specs) :-
|
|
PredIdPieces = describe_one_pred_name(ModuleInfo,
|
|
should_not_module_qualify, PredId),
|
|
strip_builtin_qualifiers_from_mode_list(ModeList, StrippedModeList),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
Name = pred_info_name(PredInfo),
|
|
MaybeDet = no,
|
|
SubDeclStr = mercury_mode_subdecl_to_string(PredOrFunc,
|
|
varset.coerce(VarSet), unqualified(Name), StrippedModeList,
|
|
MaybeDet, Context),
|
|
|
|
MainPieces = [words("In clause for")] ++ PredIdPieces ++ [suffix(":"), nl,
|
|
words("error: mode annotation specifies undeclared mode"),
|
|
quote(SubDeclStr), suffix("."), nl],
|
|
ProcIds = pred_info_all_procids(PredInfo),
|
|
(
|
|
ProcIds = [],
|
|
VerbosePieces = [words("(There are no declared modes for this"),
|
|
p_or_f(PredOrFunc), suffix(".)"), nl]
|
|
;
|
|
ProcIds = [ProcIdsHead | ProcIdsTail],
|
|
(
|
|
ProcIdsTail = [],
|
|
VerbosePieces = [words("The declared mode for this"),
|
|
p_or_f(PredOrFunc), words("is:"),
|
|
nl_indent_delta(1)] ++
|
|
mode_decl_for_pred_info_to_pieces(PredInfo, ProcIdsHead) ++
|
|
[nl_indent_delta(-1)]
|
|
;
|
|
ProcIdsTail = [_ | _],
|
|
VerbosePieces = [words("The declared modes for this"),
|
|
p_or_f(PredOrFunc), words("are the following:"),
|
|
nl_indent_delta(1)] ++
|
|
component_list_to_line_pieces(
|
|
list.map(mode_decl_for_pred_info_to_pieces(PredInfo),
|
|
ProcIds),
|
|
[]) ++
|
|
[nl_indent_delta(-1)]
|
|
)
|
|
),
|
|
Msg = simple_msg(Context,
|
|
[always(MainPieces), verbose_only(VerbosePieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- func mode_decl_for_pred_info_to_pieces(pred_info, proc_id)
|
|
= list(format_component).
|
|
|
|
mode_decl_for_pred_info_to_pieces(PredInfo, ProcId) =
|
|
[words(":- mode"), words(mode_decl_to_string(ProcId, PredInfo)),
|
|
suffix(".")].
|
|
|
|
% Clauses can have mode annotations on them, to indicate that the
|
|
% clause should only be used for particular modes of a predicate.
|
|
% This type specifies the mode annotations on a clause.
|
|
:- type mode_annotations
|
|
---> empty % No arguments.
|
|
|
|
; none % One or more arguments,
|
|
% each without any mode annotations.
|
|
|
|
; modes(list(mer_mode))
|
|
% One or more arguments, each with a mode annotation.
|
|
|
|
; mixed. % Two or more arguments, including some with mode
|
|
% annotations and some without. (This is not allowed.)
|
|
|
|
% Extract the mode annotations (if any) from a list of arguments.
|
|
%
|
|
:- pred get_mode_annotations(list(prog_term)::in, list(prog_term)::out,
|
|
mode_annotations::in, mode_annotations::out) is det.
|
|
|
|
get_mode_annotations([], [], !Annotations).
|
|
get_mode_annotations([Arg0 | Args0], [Arg | Args], !Annotations) :-
|
|
get_mode_annotation(Arg0, Arg, MaybeAnnotation),
|
|
add_annotation(MaybeAnnotation, !Annotations),
|
|
get_mode_annotations(Args0, Args, !Annotations).
|
|
|
|
:- pred add_annotation(maybe(mer_mode)::in,
|
|
mode_annotations::in, mode_annotations::out) is det.
|
|
|
|
add_annotation(no, empty, none).
|
|
add_annotation(yes(Mode), empty, modes([Mode])).
|
|
add_annotation(no, modes(_ `with_type` list(mer_mode)), mixed).
|
|
add_annotation(yes(Mode), modes(Modes), modes(Modes ++ [Mode])).
|
|
add_annotation(no, none, none).
|
|
add_annotation(yes(_), none, mixed).
|
|
add_annotation(_, mixed, mixed).
|
|
|
|
% Extract the mode annotations (if any) from a single argument.
|
|
%
|
|
:- pred get_mode_annotation(prog_term::in, prog_term::out,
|
|
maybe(mer_mode)::out) is det.
|
|
|
|
get_mode_annotation(Arg0, Arg, MaybeAnnotation) :-
|
|
(
|
|
Arg0 = term.functor(term.atom("::"), [Arg1, ModeTerm], _),
|
|
convert_mode(allow_constrained_inst_var, term.coerce(ModeTerm), Mode)
|
|
->
|
|
Arg = Arg1,
|
|
MaybeAnnotation = yes(Mode)
|
|
;
|
|
Arg = Arg0,
|
|
MaybeAnnotation = no
|
|
).
|
|
|
|
clauses_info_add_clause(ModeIds0, CVarSet, TVarSet0, Args, Body, Context,
|
|
Status, PredOrFunc, Arity, GoalType, Goal, VarSet, TVarSet,
|
|
!ClausesInfo, Warnings, !ModuleInfo, !QualInfo, !Specs) :-
|
|
!.ClausesInfo = clauses_info(VarSet0, ExplicitVarTypes0,
|
|
TVarNameMap0, InferredVarTypes, HeadVars, ClausesRep0,
|
|
RttiVarMaps, HasForeignClauses),
|
|
IsEmpty = clause_list_is_empty(ClausesRep0),
|
|
(
|
|
IsEmpty = yes,
|
|
% Create the mapping from type variable name, used to
|
|
% rename type variables occurring in explicit type
|
|
% qualifications. The version of this mapping stored
|
|
% in the clauses_info should only contain type variables
|
|
% which occur in the argument types of the predicate.
|
|
% Type variables which only occur in explicit type
|
|
% qualifications are local to the clause in which they appear.
|
|
varset.create_name_var_map(TVarSet0, TVarNameMap)
|
|
;
|
|
IsEmpty = no,
|
|
TVarNameMap = TVarNameMap0
|
|
),
|
|
update_qual_info(TVarNameMap, TVarSet0, ExplicitVarTypes0, Status,
|
|
!QualInfo),
|
|
varset.merge_subst(VarSet0, CVarSet, VarSet1, Subst),
|
|
add_clause_transform(Subst, HeadVars, Args, Body, Context, PredOrFunc,
|
|
Arity, GoalType, Goal0, VarSet1, VarSet, Warnings, !ModuleInfo,
|
|
!QualInfo, !Specs),
|
|
qual_info_get_tvarset(!.QualInfo, TVarSet),
|
|
qual_info_get_found_syntax_error(!.QualInfo, FoundError),
|
|
qual_info_set_found_syntax_error(no, !QualInfo),
|
|
(
|
|
FoundError = yes,
|
|
% Don't insert clauses containing syntax errors into the clauses_info,
|
|
% because doing that would cause typecheck.m to report spurious type
|
|
% errors. Don't report singleton variable warnings if there were
|
|
% syntax errors.
|
|
Goal = true_goal
|
|
;
|
|
FoundError = no,
|
|
Goal = Goal0,
|
|
|
|
% If we have foreign clauses, we should only add this clause
|
|
% for modes *not* covered by the foreign clauses.
|
|
(
|
|
HasForeignClauses = yes,
|
|
get_clause_list_any_order(ClausesRep0, AnyOrderClauseList),
|
|
ForeignModeIds = list.condense(list.filter_map(
|
|
(func(C) = ProcIds is semidet :-
|
|
C = clause(ProcIds, _, ClauseLang, _),
|
|
not ClauseLang = impl_lang_mercury
|
|
),
|
|
AnyOrderClauseList)),
|
|
ModeIds = list.delete_elems(ModeIds0, ForeignModeIds),
|
|
(
|
|
ModeIds = [],
|
|
ClausesRep = ClausesRep0
|
|
;
|
|
ModeIds = [_ | _],
|
|
Clause = clause(ModeIds, Goal, impl_lang_mercury, Context),
|
|
add_clause(Clause, ClausesRep0, ClausesRep)
|
|
)
|
|
;
|
|
HasForeignClauses = no,
|
|
Clause = clause(ModeIds0, Goal, impl_lang_mercury, Context),
|
|
add_clause(Clause, ClausesRep0, ClausesRep)
|
|
),
|
|
qual_info_get_var_types(!.QualInfo, ExplicitVarTypes),
|
|
!:ClausesInfo = clauses_info(VarSet, ExplicitVarTypes, TVarNameMap,
|
|
InferredVarTypes, HeadVars, ClausesRep, RttiVarMaps,
|
|
HasForeignClauses)
|
|
).
|
|
|
|
:- pred add_clause_transform(prog_substitution::in,
|
|
proc_arg_vector(prog_var)::in, list(prog_term)::in, goal::in,
|
|
prog_context::in, pred_or_func::in, arity::in, goal_type::in,
|
|
hlds_goal::out, prog_varset::in, prog_varset::out,
|
|
list(quant_warning)::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_clause_transform(Subst, HeadVars, Args0, ParseBody, Context, PredOrFunc,
|
|
Arity, GoalType, Goal, !VarSet, Warnings, !ModuleInfo,
|
|
!QualInfo, !Specs) :-
|
|
some [!SInfo] (
|
|
HeadVarList = proc_arg_vector_to_list(HeadVars),
|
|
prepare_for_head(!:SInfo),
|
|
term.apply_substitution_to_list(Args0, Subst, Args1),
|
|
substitute_state_var_mappings(Args1, Args, !VarSet, !SInfo, !Specs),
|
|
HeadGoal0 = true_goal,
|
|
( GoalType = goal_type_promise(_) ->
|
|
HeadGoal = HeadGoal0
|
|
;
|
|
ArgContext = ac_head(PredOrFunc, Arity),
|
|
insert_arg_unifications(HeadVarList, Args, Context, ArgContext,
|
|
HeadGoal0, HeadGoal1, _, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
attach_features_to_all_goals([feature_from_head],
|
|
HeadGoal1, HeadGoal)
|
|
),
|
|
prepare_for_body(FinalSVarMap, !VarSet, !SInfo),
|
|
transform_goal(ParseBody, Subst, BodyGoal, _, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
finish_goals(Context, FinalSVarMap, [HeadGoal, BodyGoal], Goal0,
|
|
!.SInfo),
|
|
qual_info_get_var_types(!.QualInfo, VarTypes0),
|
|
%
|
|
% The RTTI varmaps here are just a dummy value because the real
|
|
% ones are not introduced until typechecking and polymorphism.
|
|
%
|
|
rtti_varmaps_init(EmptyRttiVarmaps),
|
|
implicitly_quantify_clause_body(HeadVarList, Warnings, Goal0, Goal,
|
|
!VarSet, VarTypes0, VarTypes, EmptyRttiVarmaps, _),
|
|
qual_info_set_var_types(VarTypes, !QualInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
transform_goal(Goal0 - Context, Subst, hlds_goal(GoalExpr, GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_goal_2(Goal0, Context, Subst, hlds_goal(GoalExpr, GoalInfo1),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
goal_info_set_context(Context, GoalInfo1, GoalInfo).
|
|
|
|
:- pred transform_goal_2(goal_expr::in, prog_context::in,
|
|
prog_substitution::in, hlds_goal::out, num_added_goals::out,
|
|
prog_varset::in, prog_varset::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
transform_goal_2(fail_expr, _, _, hlds_goal(disj([]), GoalInfo), 0,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
goal_info_init(GoalInfo),
|
|
prepare_for_next_conjunct(set.init, !VarSet, !SInfo).
|
|
transform_goal_2(true_expr, _, _, hlds_goal(conj(plain_conj, []), GoalInfo), 0,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
goal_info_init(GoalInfo),
|
|
prepare_for_next_conjunct(set.init, !VarSet, !SInfo).
|
|
transform_goal_2(all_expr(Vars0, Goal0), Context, Subst, Goal, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
% Convert `all [Vars] Goal' into `not some [Vars] not Goal'.
|
|
TransformedGoal = not_expr(some_expr(Vars0, not_expr(Goal0) - Context)
|
|
- Context),
|
|
transform_goal_2(TransformedGoal, Context, Subst, Goal, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs).
|
|
transform_goal_2(all_state_vars_expr(StateVars, Goal0), Context, Subst,
|
|
Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_goal_2(
|
|
not_expr(some_state_vars_expr(StateVars,
|
|
not_expr(Goal0) - Context) - Context),
|
|
Context, Subst, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs).
|
|
transform_goal_2(some_expr(Vars0, Goal0), _, Subst,
|
|
hlds_goal(scope(exist_quant(Vars), Goal), GoalInfo), NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
substitute_vars(Vars0, Subst, Vars),
|
|
transform_goal(Goal0, Subst, Goal, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
goal_info_init(GoalInfo).
|
|
transform_goal_2(some_state_vars_expr(StateVars0, Goal0), _, Subst,
|
|
hlds_goal(scope(exist_quant(Vars), Goal), GoalInfo), NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
BeforeSInfo = !.SInfo,
|
|
substitute_vars(StateVars0, Subst, StateVars),
|
|
prepare_for_local_state_vars(StateVars, !VarSet, !SInfo),
|
|
transform_goal(Goal0, Subst, Goal, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
finish_local_state_vars(StateVars, Vars, BeforeSInfo, !SInfo),
|
|
goal_info_init(GoalInfo).
|
|
transform_goal_2(promise_purity_expr(Implicit, Purity, Goal0), _, Subst,
|
|
hlds_goal(
|
|
scope(promise_purity(Implicit, Purity), Goal),
|
|
GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_goal(Goal0, Subst, Goal, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
goal_info_init(GoalInfo).
|
|
transform_goal_2(
|
|
promise_equivalent_solutions_expr(Vars0, DotSVars0, ColonSVars0,
|
|
Goal0),
|
|
Context, Subst,
|
|
hlds_goal(
|
|
scope(promise_solutions(Vars, equivalent_solutions), Goal),
|
|
GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_promise_eqv_goal(Vars0, DotSVars0, ColonSVars0, Subst, Context,
|
|
Vars, Goal0, Goal, GoalInfo, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs).
|
|
transform_goal_2(
|
|
promise_equivalent_solution_sets_expr(Vars0, DotSVars0, ColonSVars0,
|
|
Goal0),
|
|
Context, Subst,
|
|
hlds_goal(
|
|
scope(promise_solutions(Vars, equivalent_solution_sets), Goal),
|
|
GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_promise_eqv_goal(Vars0, DotSVars0, ColonSVars0, Subst, Context,
|
|
Vars, Goal0, Goal, GoalInfo, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs).
|
|
transform_goal_2(
|
|
promise_equivalent_solution_arbitrary_expr(Vars0,
|
|
DotSVars0, ColonSVars0, Goal0),
|
|
Context, Subst,
|
|
hlds_goal(
|
|
scope(promise_solutions(Vars, equivalent_solution_sets_arbitrary),
|
|
Goal),
|
|
GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
transform_promise_eqv_goal(Vars0, DotSVars0, ColonSVars0, Subst, Context,
|
|
Vars, Goal0, Goal, GoalInfo, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs).
|
|
transform_goal_2(
|
|
trace_expr(MaybeCompileTime, MaybeRunTime, MaybeIO, Mutables, Goal0),
|
|
Context, Subst, hlds_goal(scope(Reason, Goal), GoalInfo), NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
list.map4(extract_trace_mutable_var(Context, !.VarSet), Mutables,
|
|
MutableHLDSs, MutableStateVars, MutableGetGoals, MutableSetGoals),
|
|
(
|
|
MaybeIO = yes(IOStateVar),
|
|
varset.lookup_name(!.VarSet, IOStateVar, IOStateVarName),
|
|
MaybeIOHLDS = yes(IOStateVarName),
|
|
extract_trace_io_var(Context, IOStateVar, IOGetGoal, IOSetGoal),
|
|
StateVars0 = [IOStateVar | MutableStateVars],
|
|
GetGoals = [IOGetGoal | MutableGetGoals],
|
|
SetGoals = [IOSetGoal | MutableSetGoals]
|
|
;
|
|
MaybeIO = no,
|
|
MaybeIOHLDS = no,
|
|
StateVars0 = MutableStateVars,
|
|
GetGoals = MutableGetGoals,
|
|
SetGoals = MutableSetGoals
|
|
),
|
|
Goal1 = goal_list_to_conj(Context, GetGoals ++ [Goal0] ++ SetGoals),
|
|
BeforeSInfo = !.SInfo,
|
|
substitute_vars(StateVars0, Subst, StateVars),
|
|
prepare_for_local_state_vars(StateVars, !VarSet, !SInfo),
|
|
transform_goal(Goal1, Subst, Goal, NumAdded1, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
NumAdded = list.length(GetGoals) + NumAdded1 + list.length(SetGoals),
|
|
finish_local_state_vars(StateVars, Vars, BeforeSInfo, !SInfo),
|
|
Reason = trace_goal(MaybeCompileTime, MaybeRunTime, MaybeIOHLDS,
|
|
MutableHLDSs, Vars),
|
|
goal_info_init(GoalInfo).
|
|
transform_goal_2(if_then_else_expr(Vars0, StateVars0, Cond0, Then0, Else0),
|
|
Context, Subst,
|
|
hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
BeforeSInfo = !.SInfo,
|
|
substitute_vars(Vars0, Subst, Vars),
|
|
substitute_vars(StateVars0, Subst, StateVars),
|
|
prepare_for_if_then_else_goal(StateVars, !VarSet, !SInfo),
|
|
transform_goal(Cond0, Subst, Cond, CondAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
finish_if_then_else_goal_condition(StateVars,
|
|
BeforeSInfo, !.SInfo, AfterCondSInfo, !:SInfo),
|
|
transform_goal(Then0, Subst, Then1, ThenAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
finish_if_then_else_goal_then_goal(StateVars, BeforeSInfo, !SInfo),
|
|
AfterThenSInfo = !.SInfo,
|
|
transform_goal(Else0, Subst, Else1, ElseAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, BeforeSInfo, !:SInfo, !Specs),
|
|
NumAdded = CondAdded + ThenAdded + ElseAdded,
|
|
goal_info_init(Context, GoalInfo),
|
|
finish_if_then_else(Context, Then1, Then, Else1, Else,
|
|
BeforeSInfo, AfterCondSInfo, AfterThenSInfo, !SInfo, !VarSet).
|
|
transform_goal_2(not_expr(SubGoal0), _, Subst,
|
|
hlds_goal(negation(SubGoal), GoalInfo),
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
BeforeSInfo = !.SInfo,
|
|
transform_goal(SubGoal0, Subst, SubGoal, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
goal_info_init(GoalInfo),
|
|
finish_negation(BeforeSInfo, !SInfo).
|
|
transform_goal_2(conj_expr(A0, B0), _, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
get_rev_conj(A0, Subst, [], R0, 0, NumAddedA,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
get_rev_conj(B0, Subst, R0, R, NumAddedA, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
L = list.reverse(R),
|
|
goal_info_init(GoalInfo),
|
|
conj_list_to_goal(L, GoalInfo, Goal).
|
|
transform_goal_2(par_conj_expr(A0, B0), _, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
get_rev_par_conj(A0, Subst, [], R0, 0, NumAddedA,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
get_rev_par_conj(B0, Subst, R0, R, NumAddedA, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
L = list.reverse(R),
|
|
goal_info_init(GoalInfo),
|
|
par_conj_list_to_goal(L, GoalInfo, Goal).
|
|
transform_goal_2(disj_expr(A0, B0), Context, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
get_disj(B0, Subst, [], L0, 0, NumAddedB,
|
|
!VarSet, !ModuleInfo, !QualInfo, !.SInfo, !Specs),
|
|
get_disj(A0, Subst, L0, L1, NumAddedB, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !.SInfo, !Specs),
|
|
finish_disjunction(Context, !.VarSet, L1, L, !:SInfo),
|
|
goal_info_init(Context, GoalInfo),
|
|
disj_list_to_goal(L, GoalInfo, Goal).
|
|
transform_goal_2(implies_expr(P, Q), Context, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
% `P => Q' is defined as `not (P, not Q)'
|
|
TransformedGoal = not_expr(conj_expr(P, not_expr(Q) - Context) - Context),
|
|
transform_goal_2(TransformedGoal, Context, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs).
|
|
transform_goal_2(equivalent_expr(P0, Q0), _, Subst, Goal, NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
% `P <=> Q' is defined as `(P => Q), (Q => P)',
|
|
% but that transformation must not be done until
|
|
% after quantification analysis, lest the duplication of
|
|
% the goals concerned affect the implicit quantification
|
|
% of the variables inside them.
|
|
|
|
BeforeSInfo = !.SInfo,
|
|
goal_info_init(GoalInfo),
|
|
transform_goal(P0, Subst, P, NumAddedP, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
transform_goal(Q0, Subst, Q, NumAddedQ, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
NumAdded = NumAddedP + NumAddedQ,
|
|
Goal = hlds_goal(shorthand(bi_implication(P, Q)), GoalInfo),
|
|
finish_equivalence(BeforeSInfo, !SInfo).
|
|
transform_goal_2(event_expr(EventName, Args0), Context, Subst, Goal,
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
Args1 = expand_bang_state_var_args(Args0),
|
|
prepare_for_call(!SInfo),
|
|
term.apply_substitution_to_list(Args1, Subst, Args),
|
|
make_fresh_arg_vars(Args, HeadVars, !VarSet, !SInfo, !Specs),
|
|
list.length(HeadVars, Arity),
|
|
list.duplicate(Arity, in_mode, Modes),
|
|
goal_info_init(Context, GoalInfo),
|
|
Details = event_call(EventName),
|
|
GoalExpr0 = generic_call(Details, HeadVars, Modes, detism_det),
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo),
|
|
CallId = generic_call_id(gcid_event_call(EventName)),
|
|
insert_arg_unifications(HeadVars, Args, Context, ac_call(CallId),
|
|
Goal0, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo).
|
|
transform_goal_2(call_expr(Name, Args0, Purity), Context, Subst, Goal,
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
Args1 = expand_bang_state_var_args(Args0),
|
|
(
|
|
Name = unqualified("\\="),
|
|
Args1 = [LHS, RHS]
|
|
->
|
|
prepare_for_call(!SInfo),
|
|
% `LHS \= RHS' is defined as `not (LHS = RHS)'
|
|
transform_goal_2(not_expr(unify_expr(LHS, RHS, Purity) - Context),
|
|
Context, Subst, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo)
|
|
;
|
|
% check for a state var record assignment:
|
|
% !Var ^ field := Value
|
|
Name = unqualified(":="),
|
|
Args1 = [LHS0, RHS0],
|
|
LHS0 = functor(atom("^"), [StateVar0, Remainder], FieldListContext),
|
|
StateVar0 = functor(atom("!"), Args @ [variable(_, _)],
|
|
StateVarContext)
|
|
->
|
|
prepare_for_call(!SInfo),
|
|
% !Var ^ field := Value is defined as !:Var = !.Var ^ field := Value.
|
|
LHS = functor(atom("!:"), Args, StateVarContext),
|
|
StateVar = functor(atom("!."), Args, StateVarContext),
|
|
FieldList = functor(atom("^"), [StateVar, Remainder],
|
|
FieldListContext),
|
|
RHS = functor(atom(":="), [FieldList, RHS0], Context),
|
|
transform_goal_2(unify_expr(LHS, RHS, Purity),
|
|
Context, Subst, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo)
|
|
;
|
|
% check for a DCG field access goal:
|
|
% get: Field =^ field
|
|
% set: ^ field := Field
|
|
( Name = unqualified(Operator) ),
|
|
( Operator = "=^"
|
|
; Operator = ":="
|
|
)
|
|
->
|
|
prepare_for_call(!SInfo),
|
|
term.apply_substitution_to_list(Args1, Subst, Args2),
|
|
transform_dcg_record_syntax(Operator, Args2, Context, Goal, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo)
|
|
;
|
|
prepare_for_call(!SInfo),
|
|
term.apply_substitution_to_list(Args1, Subst, Args),
|
|
make_fresh_arg_vars(Args, HeadVars, !VarSet, !SInfo, !Specs),
|
|
list.length(Args, Arity),
|
|
(
|
|
% Check for a higher-order call,
|
|
% i.e. a call to either call/N or ''/N.
|
|
( Name = unqualified("call")
|
|
; Name = unqualified("")
|
|
),
|
|
HeadVars = [PredVar | RealHeadVars]
|
|
->
|
|
% Initialize some fields to junk.
|
|
Modes = [],
|
|
Det = detism_erroneous,
|
|
|
|
GenericCall = higher_order(PredVar, Purity, pf_predicate, Arity),
|
|
Call = generic_call(GenericCall, RealHeadVars, Modes, Det),
|
|
|
|
hlds_goal.generic_call_id(GenericCall, CallId)
|
|
;
|
|
% Initialize some fields to junk.
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
|
|
MaybeUnifyContext = no,
|
|
Call = plain_call(PredId, ModeId, HeadVars, not_builtin,
|
|
MaybeUnifyContext, Name),
|
|
CallId = plain_call_id(simple_call_id(pf_predicate, Name, Arity))
|
|
),
|
|
goal_info_init(Context, GoalInfo0),
|
|
goal_info_set_purity(Purity, GoalInfo0, GoalInfo),
|
|
Goal0 = hlds_goal(Call, GoalInfo),
|
|
|
|
record_called_pred_or_func(pf_predicate, Name, Arity, !QualInfo),
|
|
insert_arg_unifications(HeadVars, Args, Context, ac_call(CallId),
|
|
Goal0, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo)
|
|
).
|
|
transform_goal_2(unify_expr(A0, B0, Purity), Context, Subst, Goal,
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
% It is an error for the left or right hand side of a
|
|
% unification to be !X (it may be !.X or !:X, however).
|
|
( A0 = functor(atom("!"), [variable(StateVarA, _)], _) ->
|
|
report_svar_unify_error(Context, !.VarSet, StateVarA, !Specs),
|
|
Goal = true_goal,
|
|
NumAdded = 0
|
|
; B0 = functor(atom("!"), [variable(StateVarB, _)], _) ->
|
|
report_svar_unify_error(Context, !.VarSet, StateVarB, !Specs),
|
|
Goal = true_goal,
|
|
NumAdded = 0
|
|
;
|
|
prepare_for_call(!SInfo),
|
|
term.apply_substitution(A0, Subst, A),
|
|
term.apply_substitution(B0, Subst, B),
|
|
unravel_unification(A, B, Context, umc_explicit, [], Purity, Goal,
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
finish_call(!VarSet, !SInfo)
|
|
).
|
|
|
|
:- pred extract_trace_mutable_var(prog_context::in, prog_varset::in,
|
|
trace_mutable_var::in, trace_mutable_var_hlds::out,
|
|
prog_var::out, goal::out, goal::out) is det.
|
|
|
|
extract_trace_mutable_var(Context, VarSet, Mutable, MutableHLDS, StateVar,
|
|
GetGoal, SetGoal) :-
|
|
Mutable = trace_mutable_var(MutableName, StateVar),
|
|
varset.lookup_name(VarSet, StateVar, StateVarName),
|
|
MutableHLDS = trace_mutable_var_hlds(MutableName, StateVarName),
|
|
GetPredName = unqualified("get_" ++ MutableName),
|
|
SetPredName = unqualified("set_" ++ MutableName),
|
|
SetVar = functor(atom("!:"), [variable(StateVar, Context)], Context),
|
|
UseVar = functor(atom("!."), [variable(StateVar, Context)], Context),
|
|
GetPurity = purity_semipure,
|
|
SetPurity = purity_impure,
|
|
GetGoal = call_expr(GetPredName, [SetVar], GetPurity) - Context,
|
|
SetGoal = call_expr(SetPredName, [UseVar], SetPurity) - Context.
|
|
|
|
:- pred extract_trace_io_var(prog_context::in, prog_var::in,
|
|
goal::out, goal::out) is det.
|
|
|
|
extract_trace_io_var(Context, StateVar, GetGoal, SetGoal) :-
|
|
Builtin = mercury_private_builtin_module,
|
|
GetPredName = qualified(Builtin, "trace_get_io_state"),
|
|
SetPredName = qualified(Builtin, "trace_set_io_state"),
|
|
SetVar = functor(atom("!:"), [variable(StateVar, Context)], Context),
|
|
UseVar = functor(atom("!."), [variable(StateVar, Context)], Context),
|
|
GetPurity = purity_semipure,
|
|
SetPurity = purity_impure,
|
|
GetGoal = call_expr(GetPredName, [SetVar], GetPurity) - Context,
|
|
SetGoal = call_expr(SetPredName, [UseVar], SetPurity) - Context.
|
|
|
|
:- pred transform_promise_eqv_goal(prog_vars::in, prog_vars::in, prog_vars::in,
|
|
prog_substitution::in, prog_context::in, prog_vars::out,
|
|
goal::in, hlds_goal::out, hlds_goal_info::out, int::out,
|
|
prog_varset::in, prog_varset::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out, svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
transform_promise_eqv_goal(Vars0, DotSVars0, ColonSVars0, Subst, Context, Vars,
|
|
Goal0, Goal, GoalInfo, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs) :-
|
|
substitute_vars(Vars0, Subst, Vars1),
|
|
substitute_vars(DotSVars0, Subst, DotSVars1),
|
|
convert_dot_state_vars(Context, DotSVars1, DotSVars, !VarSet,
|
|
!SInfo, !Specs),
|
|
transform_goal(Goal0, Subst, Goal, NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
goal_info_init(GoalInfo),
|
|
substitute_vars(ColonSVars0, Subst, ColonSVars1),
|
|
convert_dot_state_vars(Context, ColonSVars1, ColonSVars, !VarSet,
|
|
!SInfo, !Specs),
|
|
Vars = Vars1 ++ DotSVars ++ ColonSVars.
|
|
|
|
:- pred convert_dot_state_vars(prog_context::in, prog_vars::in, prog_vars::out,
|
|
prog_varset::in, prog_varset::out, svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
convert_dot_state_vars(_Context, [], [], !VarSet, !SInfo, !Specs).
|
|
convert_dot_state_vars(Context, [Dot0 | Dots0], [Dot | Dots],
|
|
!VarSet, !SInfo, !Specs) :-
|
|
dot(Context, Dot0, Dot, !VarSet, !SInfo, !Specs),
|
|
convert_dot_state_vars(Context, Dots0, Dots, !VarSet, !SInfo, !Specs).
|
|
|
|
:- pred convert_colon_state_vars(prog_context::in,
|
|
prog_vars::in, prog_vars::out, prog_varset::in, prog_varset::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
convert_colon_state_vars(_Context, [], [], !VarSet, !SInfo, !Specs).
|
|
convert_colon_state_vars(Context, [Colon0 | Colons0], [Colon | Colons],
|
|
!VarSet, !SInfo, !Specs) :-
|
|
colon(Context, Colon0, Colon, !VarSet, !SInfo, !Specs),
|
|
convert_colon_state_vars(Context, Colons0, Colons, !VarSet,
|
|
!SInfo, !Specs).
|
|
|
|
:- pred report_svar_unify_error(prog_context::in, prog_varset::in, svar::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_svar_unify_error(Context, VarSet, StateVar, !Specs) :-
|
|
Name = varset.lookup_name(VarSet, StateVar),
|
|
Pieces = [nl, words("Error:"), fixed("!" ++ Name),
|
|
words("cannot appear as a unification argument."), nl,
|
|
words("You probably meant"), fixed("!." ++ Name),
|
|
words("or"), fixed("!:" ++ Name), suffix(".")],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- inst dcg_record_syntax_op == bound("=^"; ":=").
|
|
|
|
:- pred transform_dcg_record_syntax(string::in(dcg_record_syntax_op),
|
|
list(prog_term)::in, prog_context::in, hlds_goal::out, int::out,
|
|
prog_varset::in, prog_varset::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
transform_dcg_record_syntax(Operator, ArgTerms0, Context, Goal, NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
goal_info_init(Context, GoalInfo),
|
|
(
|
|
ArgTerms0 = [LHSTerm, RHSTerm, TermInputTerm, TermOutputTerm],
|
|
(
|
|
Operator = "=^",
|
|
AccessType = get,
|
|
FieldNameTerm = RHSTerm,
|
|
FieldValueTerm = LHSTerm
|
|
;
|
|
Operator = ":=",
|
|
AccessType = set,
|
|
LHSTerm = term.functor(term.atom("^"), [FieldNameTerm0], _),
|
|
FieldNameTerm = FieldNameTerm0,
|
|
FieldValueTerm = RHSTerm
|
|
)
|
|
->
|
|
parse_field_list(FieldNameTerm, MaybeFieldNames),
|
|
(
|
|
MaybeFieldNames = ok1(FieldNames),
|
|
ArgTerms = [FieldValueTerm, TermInputTerm, TermOutputTerm],
|
|
transform_dcg_record_syntax_2(AccessType, FieldNames, ArgTerms,
|
|
Context, Goal, NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
!SInfo, !Specs)
|
|
;
|
|
MaybeFieldNames = error1(Errors),
|
|
invalid_goal("^", ArgTerms0, GoalInfo, Goal, !VarSet,
|
|
!SInfo, !Specs),
|
|
NumAdded = 0,
|
|
qual_info_set_found_syntax_error(yes, !QualInfo),
|
|
list.foldl(report_dcg_field_error(Context, AccessType, !.VarSet),
|
|
Errors, !Specs)
|
|
)
|
|
;
|
|
invalid_goal("^", ArgTerms0, GoalInfo, Goal, !VarSet, !SInfo, !Specs),
|
|
NumAdded = 0,
|
|
qual_info_set_found_syntax_error(yes, !QualInfo),
|
|
Pieces = [words("Error: expected `Field =^ field1 ^ ... ^ fieldN'"),
|
|
words("or `^ field1 ^ ... ^ fieldN := Field'"),
|
|
words("in DCG field access goal."), nl],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs]
|
|
).
|
|
|
|
:- pred report_dcg_field_error(term.context::in, field_access_type::in,
|
|
prog_varset::in, pair(string, term(prog_var_type))::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_dcg_field_error(Context, AccessType, VarSet, Error, !Specs) :-
|
|
Error = ErrorMsg - ErrorTerm,
|
|
(
|
|
AccessType = set,
|
|
Action = "update"
|
|
;
|
|
AccessType = get,
|
|
Action = "extraction"
|
|
),
|
|
GenericVarSet = varset.coerce(VarSet),
|
|
TermStr = mercury_term_to_string(GenericVarSet, no, ErrorTerm),
|
|
Pieces = [words("In DCG field"), words(Action), words("goal:"), nl,
|
|
words("error:"), words(ErrorMsg), words("at term"),
|
|
quote(TermStr), suffix("."), nl],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
% Produce an invalid goal.
|
|
%
|
|
:- pred invalid_goal(string::in, list(prog_term)::in, hlds_goal_info::in,
|
|
hlds_goal::out, prog_varset::in, prog_varset::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
invalid_goal(UpdateStr, Args0, GoalInfo, Goal, !VarSet, !SInfo, !Specs) :-
|
|
make_fresh_arg_vars(Args0, HeadVars, !VarSet, !SInfo, !Specs),
|
|
MaybeUnifyContext = no,
|
|
GoalExpr = plain_call(invalid_pred_id, invalid_proc_id, HeadVars,
|
|
not_builtin, MaybeUnifyContext, unqualified(UpdateStr)),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred transform_dcg_record_syntax_2(field_access_type::in, field_list::in,
|
|
list(prog_term)::in, prog_context::in, hlds_goal::out,
|
|
num_added_goals::out, prog_varset::in, prog_varset::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
transform_dcg_record_syntax_2(AccessType, FieldNames, ArgTerms, Context, Goal,
|
|
NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
make_fresh_arg_vars(ArgTerms, ArgVars, !VarSet, !SInfo, !Specs),
|
|
( ArgVars = [FieldValueVar, TermInputVar, TermOutputVar] ->
|
|
(
|
|
AccessType = set,
|
|
expand_set_field_function_call(Context, umc_explicit, [],
|
|
FieldNames, FieldValueVar, TermInputVar, TermOutputVar,
|
|
Functor, InnermostFunctor - InnermostSubContext, Goal0,
|
|
SetAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
|
|
FieldArgNumber = 2,
|
|
FieldArgContext = ac_functor(InnermostFunctor, umc_explicit,
|
|
InnermostSubContext),
|
|
InputTermArgNumber = 1,
|
|
InputTermArgContext = ac_functor(Functor, umc_explicit, []),
|
|
( Functor = cons(FuncName0, FuncArity0) ->
|
|
FuncName = FuncName0,
|
|
FuncArity = FuncArity0
|
|
;
|
|
unexpected(this_file, "transform_dcg_record_syntax_2")
|
|
),
|
|
% DCG arguments should always be distinct variables,
|
|
% so this context should never be used.
|
|
OutputTermArgNumber = 3,
|
|
SimpleCallId = simple_call_id(pf_function, FuncName, FuncArity),
|
|
OutputTermArgContext = ac_call(plain_call_id(SimpleCallId)),
|
|
|
|
ArgContexts = [
|
|
FieldArgNumber - FieldArgContext,
|
|
InputTermArgNumber - InputTermArgContext,
|
|
OutputTermArgNumber - OutputTermArgContext
|
|
],
|
|
insert_arg_unifications_with_supplied_contexts(ArgVars, ArgTerms,
|
|
ArgContexts, Context, Goal0, Goal, ArgAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
NumAdded = SetAdded + ArgAdded
|
|
;
|
|
AccessType = get,
|
|
expand_dcg_field_extraction_goal(Context, umc_explicit, [],
|
|
FieldNames, FieldValueVar, TermInputVar, TermOutputVar,
|
|
Functor, InnermostFunctor - _InnerSubContext, Goal0,
|
|
ExtractAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
InputTermArgNumber = 1,
|
|
InputTermArgContext = ac_functor(Functor, umc_explicit, []),
|
|
|
|
( InnermostFunctor = cons(FuncName0, FuncArity0) ->
|
|
FuncName = FuncName0,
|
|
FuncArity = FuncArity0
|
|
;
|
|
unexpected(this_file, "transform_dcg_record_syntax_2")
|
|
),
|
|
FieldArgNumber = 2,
|
|
SimpleCallId = simple_call_id(pf_function, FuncName, FuncArity),
|
|
FieldArgContext = ac_call(plain_call_id(SimpleCallId)),
|
|
|
|
% DCG arguments should always be distinct variables,
|
|
% so this context should never be used.
|
|
OutputTermArgNumber = 1,
|
|
OutputTermArgContext = ac_functor(Functor, umc_explicit, []),
|
|
ArgContexts = [
|
|
FieldArgNumber - FieldArgContext,
|
|
InputTermArgNumber - InputTermArgContext,
|
|
OutputTermArgNumber - OutputTermArgContext
|
|
],
|
|
insert_arg_unifications_with_supplied_contexts(ArgVars, ArgTerms,
|
|
ArgContexts, Context, Goal0, Goal, ArgAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
NumAdded = ExtractAdded + ArgAdded
|
|
)
|
|
;
|
|
unexpected(this_file, "do_transform_dcg_record_syntax")
|
|
).
|
|
|
|
% get_rev_conj(Goal, Subst, RevConj0, RevConj) :
|
|
%
|
|
% Goal is a tree of conjuncts. Flatten it into a list (applying Subst),
|
|
% reverse it, append RevConj0, and return the result in RevConj.
|
|
%
|
|
:- pred get_rev_conj(goal::in, prog_substitution::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out, int::in, int::out,
|
|
prog_varset::in, prog_varset::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out, svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
get_rev_conj(Goal, Subst, RevConj0, RevConj, !NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs) :-
|
|
( Goal = conj_expr(A, B) - _Context ->
|
|
get_rev_conj(A, Subst, RevConj0, RevConj1, !NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
get_rev_conj(B, Subst, RevConj1, RevConj, !NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs)
|
|
;
|
|
transform_goal(Goal, Subst, Goal1, GoalAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
!:NumAdded = !.NumAdded + GoalAdded,
|
|
goal_to_conj_list(Goal1, ConjList),
|
|
RevConj = list.reverse(ConjList) ++ RevConj0
|
|
).
|
|
|
|
% get_rev_par_conj(Goal, Subst, RevParConj0, RevParConj) :
|
|
%
|
|
% Goal is a tree of conjuncts. Flatten it into a list (applying Subst),
|
|
% reverse it, append RevParConj0, and return the result in RevParConj.
|
|
%
|
|
:- pred get_rev_par_conj(goal::in, prog_substitution::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out, int::in, int::out,
|
|
prog_varset::in, prog_varset::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out, svar_info::in, svar_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
get_rev_par_conj(Goal, Subst, RevParConj0, RevParConj, !NumAdded, !VarSet,
|
|
!ModuleInfo, !QualInfo, !SInfo, !Specs) :-
|
|
( Goal = par_conj_expr(A, B) - _Context ->
|
|
get_rev_par_conj(A, Subst, RevParConj0, RevParConj1, !NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
|
|
get_rev_par_conj(B, Subst, RevParConj1, RevParConj, !NumAdded,
|
|
!VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs)
|
|
;
|
|
transform_goal(Goal, Subst, Goal1, GoalAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, !SInfo, !Specs),
|
|
!:NumAdded = !.NumAdded + GoalAdded,
|
|
goal_to_par_conj_list(Goal1, ParConjList),
|
|
RevParConj = list.reverse(ParConjList) ++ RevParConj0
|
|
).
|
|
|
|
% get_disj(Goal, Subst, Disj0, Disj):
|
|
%
|
|
% Goal is a tree of disjuncts. Flatten it into a list (applying Subst),
|
|
% append Disj0, and return the result in Disj.
|
|
%
|
|
:- pred get_disj(goal::in, prog_substitution::in,
|
|
hlds_goal_svar_infos::in, hlds_goal_svar_infos::out, int::in, int::out,
|
|
prog_varset::in, prog_varset::out, module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out, svar_info::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
get_disj(Goal, Subst, Disj0, Disj, !NumAdded, !VarSet, !ModuleInfo, !QualInfo,
|
|
SInfo, !Specs) :-
|
|
( Goal = disj_expr(A, B) - _Context ->
|
|
get_disj(B, Subst, Disj0, Disj1, !NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, SInfo, !Specs),
|
|
get_disj(A, Subst, Disj1, Disj, !NumAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, SInfo, !Specs)
|
|
;
|
|
transform_goal(Goal, Subst, Goal1, GoalAdded, !VarSet, !ModuleInfo,
|
|
!QualInfo, SInfo, SInfo1, !Specs),
|
|
!:NumAdded = !.NumAdded + GoalAdded,
|
|
Disj = [{Goal1, SInfo1} | Disj0]
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "add_clause.m".
|
|
|
|
%----------------------------------------------------------------------------%
|