mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 19:03:45 +00:00
The language manual guarantees this order, and we do *invoke* initializer
functions in this order, but we used to *define* them in reverse order
if they came from mutable declarations. This diff fixes that.
compiler/make_hlds_passes.m:
The code for gathering the foreign_proc_exports (FPEs) used to implement
mutables returned them as a list in source order. However, the code
in make_hlds_passes.m itself treated that list as it were in reverse order
(probably as a relic of a time when it *was* in reverse order), adding
new entries to at the front to implement any non-mutable initialize (and
finalise) declarations, and finally "unreversing" it. This "unreversing"
of course *reversed* the originally in-order part. (The order of their
*invocation* is controlled by a different mechanism.)
Fix this by *consistently* using cords, instead of reversed lists,
for everything in this module that needs appending at the logical "end".
Do this even for kinds of items that were not involved in this bug.
compiler/add_mutable_aux_preds.m:
Return the list foreign_proc_exports created as a cord, to allow later
operations to append to its end.
compiler/add_pragma.m:
Return the list of tabled pragmas whose addition to the HLDS is deferred
until a slightly later pass as a cord.
1226 lines
54 KiB
Mathematica
1226 lines
54 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1993-2012 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.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This module creates the initial HLDS from the augmented parse tree
|
|
% of a module.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module hlds.make_hlds.make_hlds_passes.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.make_hlds.make_hlds_types.
|
|
:- import_module hlds.make_hlds.qual_info.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.equiv_type.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.module_qual.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_used_modules.
|
|
:- import_module parse_tree.prog_item.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% parse_tree_to_hlds(AugCompUnit, Globals, DumpBaseFileName, MQInfo,
|
|
% TypeEqvMap, UsedModules, QualInfo, InvalidTypes, InvalidModes,
|
|
% HLDS, Specs):
|
|
%
|
|
% Given MQInfo (returned by module_qual.m) and TypeEqvMap and UsedModules
|
|
% (both returned by equiv_type.m), convert AugCompUnit to HLDS.
|
|
% Return any errors found in Specs.
|
|
% Return InvalidTypes = yes if we found undefined types.
|
|
% Return InvalidModes = yes if we found undefined or cyclic insts or modes.
|
|
% QualInfo is an abstract type that check_typeclass.m will later pass
|
|
% to produce_instance_method_clauses.
|
|
%
|
|
:- pred parse_tree_to_hlds(aug_compilation_unit::in, globals::in,
|
|
string::in, mq_info::in, type_eqv_map::in, used_modules::in,
|
|
qual_info::out, found_invalid_type::out, found_invalid_inst_or_mode::out,
|
|
module_info::out, list(error_spec)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- include_module hlds.make_hlds.make_hlds_passes.make_hlds_separate_items.
|
|
|
|
:- import_module hlds.add_pred.
|
|
:- import_module hlds.add_special_pred.
|
|
:- import_module hlds.default_func_mode.
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.make_hlds.add_class.
|
|
:- import_module hlds.make_hlds.add_clause.
|
|
:- import_module hlds.make_hlds.add_foreign_proc.
|
|
:- import_module hlds.make_hlds.add_mode.
|
|
:- import_module hlds.make_hlds.add_mutable_aux_preds.
|
|
:- import_module hlds.make_hlds.add_pragma.
|
|
:- import_module hlds.make_hlds.add_solver.
|
|
:- import_module hlds.make_hlds.add_type.
|
|
:- import_module hlds.make_hlds.make_hlds_passes.make_hlds_separate_items.
|
|
:- import_module hlds.make_hlds.make_hlds_warn.
|
|
:- import_module hlds.pred_name.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module hlds.special_pred.
|
|
:- import_module hlds.status.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.get_dependencies.
|
|
:- import_module parse_tree.maybe_error.
|
|
:- import_module parse_tree.prog_foreign.
|
|
:- import_module parse_tree.prog_item_stats.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.prog_util.
|
|
:- import_module recompilation.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module io.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module term_context.
|
|
:- import_module term_subst.
|
|
:- import_module varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
parse_tree_to_hlds(AugCompUnit, Globals, DumpBaseFileName, MQInfo0,
|
|
TypeEqvMap, UsedModules, !:QualInfo,
|
|
!:FoundInvalidType, !:FoundInvalidInstOrMode, !:ModuleInfo, !:Specs) :-
|
|
ParseTreeModuleSrc = AugCompUnit ^ acu_module_src,
|
|
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
|
|
ModuleNameContext = ParseTreeModuleSrc ^ ptms_module_name_context,
|
|
get_implicit_avail_needs_in_aug_compilation_unit(Globals, AugCompUnit,
|
|
ImplicitlyUsedModules),
|
|
mq_info_get_partial_qualifier_info(MQInfo0, PQInfo),
|
|
|
|
% Optionally gather statistics about the items in the compilation unit.
|
|
trace [compile_time(flag("item_stats")), io(!IO)] (
|
|
% We *append* statistics to a file, rather than simply *writing*
|
|
% them to a file, so that we can gather statistics from a sequence of
|
|
% Mercury compiler invocations, such as those in a bootcheck.
|
|
% The file should be created empty before the start of the sequence,
|
|
% and should be appended to by one Mercury compiler invocation
|
|
% at a time. (We don't do any locking, so if more than one invocation
|
|
% tries to append to the file at the same time, the resulting output
|
|
% will be malformed.)
|
|
%
|
|
% You may of course change the name of the file, if you wish.
|
|
%
|
|
% A statistics file resulting from one or more compiler invocations
|
|
% may be analyzed using tools/item_stats.
|
|
|
|
io.open_append("/tmp/ITEM_STATS", Res, !IO),
|
|
(
|
|
Res = error(_)
|
|
;
|
|
Res = ok(Stream),
|
|
gather_and_write_item_stats(Stream, AugCompUnit, !IO),
|
|
io.close_output(Stream, !IO)
|
|
)
|
|
),
|
|
|
|
TypeSpecs = ParseTreeModuleSrc ^ ptms_type_specs,
|
|
InstModeSpecs = ParseTreeModuleSrc ^ ptms_inst_mode_specs,
|
|
!:Specs = TypeSpecs ++ InstModeSpecs,
|
|
|
|
IsInvalidTypeSpec =
|
|
( pred(Spec::in) is semidet :-
|
|
extract_spec_phase(Spec, Phase),
|
|
Phase = phase_type_inst_mode_check_invalid_type
|
|
),
|
|
IsInvalidInstModeSpec =
|
|
( pred(Spec::in) is semidet :-
|
|
extract_spec_phase(Spec, Phase),
|
|
Phase = phase_type_inst_mode_check_invalid_inst_mode
|
|
),
|
|
list.filter(IsInvalidTypeSpec, TypeSpecs, InvalidTypeSpecs),
|
|
list.filter(IsInvalidInstModeSpec, InstModeSpecs, InvalidInstModeSpecs),
|
|
TypeErrors = contains_errors(Globals, InvalidTypeSpecs),
|
|
InstModeErrors = contains_errors(Globals, InvalidInstModeSpecs),
|
|
(
|
|
TypeErrors = no,
|
|
!:FoundInvalidType = did_not_find_invalid_type
|
|
;
|
|
TypeErrors = yes,
|
|
!:FoundInvalidType = found_invalid_type
|
|
),
|
|
(
|
|
InstModeErrors = no,
|
|
!:FoundInvalidInstOrMode = did_not_find_invalid_inst_or_mode
|
|
;
|
|
InstModeErrors = yes,
|
|
!:FoundInvalidInstOrMode = found_invalid_inst_or_mode
|
|
),
|
|
|
|
% We used to add items to the HLDS in three passes.
|
|
% Roughly,
|
|
%
|
|
% - pass 1 added type, inst and mode definitions and predicate
|
|
% declarations to the module,
|
|
% - pass 3 added the definitions of predicates, including not just
|
|
% clauses but also clause-like pragmas such as foreign_procs,
|
|
% to the module, while
|
|
% - pass 2 did the tasks that had to be done before pass 3 started
|
|
% but which needed access to *all* the declarations added by pass 1,
|
|
% not just the ones processed so far.
|
|
%
|
|
% We now add items to the HLDS by item kind: first one kind of item,
|
|
% then another, then another. The general order remains roughly the same
|
|
% as before, but we now also impose an order *within* what used to be
|
|
% a pass. For example, we add all type definitions before we add
|
|
% any inst definitions, since inst definitions may now refer to type
|
|
% constructors.
|
|
%
|
|
% The constraints of what we have to add before what are documented below.
|
|
|
|
separate_items_in_aug_comp_unit(AugCompUnit, InclMap, Avails, FIMs,
|
|
TypeDefnsAbstract, TypeDefnsMercury, TypeDefnsForeign,
|
|
InstDefns, ModeDefns, PredDecls, ModeDecls,
|
|
Promises, Typeclasses, Instances, Initialises, Finalises, Mutables,
|
|
TypeRepnMap, ForeignEnums, ForeignExportEnums,
|
|
PragmasDecl, PragmasDeclTypeSpec,
|
|
PragmasDeclTermInfo, PragmasDeclTerm2Info,
|
|
PragmasDeclSharing, PragmasDeclReuse,
|
|
PragmasImpl,
|
|
PragmasGenUnusedArgs, PragmasGenExceptions,
|
|
PragmasGenTrailing, PragmasGenMMTabling,
|
|
Clauses, IntBadClauses),
|
|
|
|
map.init(DirectArgMap),
|
|
TypeRepnDec = type_repn_decision_data(TypeRepnMap, DirectArgMap,
|
|
ForeignEnums, ForeignExportEnums),
|
|
module_info_init(Globals, ModuleName, ModuleNameContext, DumpBaseFileName,
|
|
InclMap, UsedModules, ImplicitlyUsedModules, PQInfo, no, TypeRepnDec,
|
|
!:ModuleInfo),
|
|
|
|
% The old pass 1.
|
|
|
|
% Record the import_module and use_module declarations.
|
|
add_item_avails(Avails, set.init, AncestorAvailModules, !ModuleInfo),
|
|
module_info_get_ancestor_avail_modules(!.ModuleInfo, AncestorAvailSet0),
|
|
set.union(AncestorAvailModules, AncestorAvailSet0, AncestorAvailSet),
|
|
module_info_set_ancestor_avail_modules(AncestorAvailSet, !ModuleInfo),
|
|
|
|
% Record type definitions.
|
|
%
|
|
% We first add the abstract type definitions, then the nonabstract ones,
|
|
% since this should simplify the processing of type constructors that
|
|
% have both abstract and nonabstract definitions. Among the nonabstract
|
|
% type definitions, we process the ones that give Mercury definitions
|
|
% before the ones that give foreign language definitions, since the code
|
|
% that adds foreign language definitions can give the right error message
|
|
% only in that case.
|
|
%
|
|
% The definition of a solver type requires the declaration and the
|
|
% definition of several auxiliary predicates, for representation changes
|
|
% and maybe for initialization. When add_type_defn defines a solver type,
|
|
% it returns the declarations of the auxiliary predicates that implement
|
|
% that solver type, and if the solver type is defined in the current
|
|
% module, then they also return the foreign procs and mutables that
|
|
% implement those auxiliary predicates. We add these items to the HLDS
|
|
% later, at the time when we add items of the same kinds to the HLDS.
|
|
% (The mutables implement the constraint store.)
|
|
some [!SolverPredDeclCord, !SolverForeignProcCord, !SolverMutableCord]
|
|
(
|
|
!:SolverPredDeclCord = cord.init,
|
|
!:SolverForeignProcCord = cord.init,
|
|
!:SolverMutableCord = cord.init,
|
|
% XXX TYPE_REPN
|
|
% The XXX comments below are irrelevant if
|
|
% separate_items_in_aug_comp_unit returns a type_ctor_checked_map.
|
|
% XXX TYPE_REPN
|
|
% - group parse_tree_{du,eqv,solver}_type together in their type;
|
|
% - have separate_items_in_aug_comp_unit return the definitions of
|
|
% (a) abstract, (b) du/eqv/solver and (c) foreign types in lists
|
|
% whose elements are of different, specialized types; and
|
|
% - call specialized versions of add_type_defn in each foldl5 below.
|
|
% XXX TYPE_REPN Consider treating du/eqv/solver type defns separately,
|
|
% but beware: the current code of maybe_make_abstract_type_defn,
|
|
% while usually returning abstract types, sometimes returns Mercury
|
|
% types.
|
|
add_type_defns(TypeDefnsAbstract,
|
|
!ModuleInfo, !FoundInvalidType, !Specs, !SolverPredDeclCord,
|
|
!SolverForeignProcCord, !SolverMutableCord),
|
|
add_type_defns(TypeDefnsMercury,
|
|
!ModuleInfo, !FoundInvalidType, !Specs, !SolverPredDeclCord,
|
|
!SolverForeignProcCord, !SolverMutableCord),
|
|
add_type_defns(TypeDefnsForeign,
|
|
!ModuleInfo, !FoundInvalidType, !Specs, !SolverPredDeclCord,
|
|
!SolverForeignProcCord, !SolverMutableCord),
|
|
% We use cords to record new predicate declarations, foreign_procs
|
|
% and mutables. This preserves order without quadratic behavior.
|
|
% Preserving order can be important, because if e.g. a pred decl
|
|
% for a solver type from a later type defn could come before
|
|
% a pred decl from an earlier type defn, but the two type definitions
|
|
% are actually the same, with one copy being from a module's .intN
|
|
% file and the other copy being from the module's .opt file.
|
|
% The reason why not preserving order would be a problem is that
|
|
% we suppress error messages about duplicate pred decls only if
|
|
% the duplicate (i.e. the copy we see second) is from a .opt file,
|
|
%
|
|
% The code of make_hlds_separate_items.m gives us lists of type defns
|
|
% in which type_defns from .intN files will always precede
|
|
% type_defns from .opt files. To ensure that this property also holds
|
|
% for the pred decls of any auxiliary predicates needed for the
|
|
% solver types among them, we must preserve the original order.
|
|
SolverPredDecls = cord.list(!.SolverPredDeclCord),
|
|
SolverForeignProcs = cord.list(!.SolverForeignProcCord),
|
|
SolverMutables = cord.list(!.SolverMutableCord)
|
|
),
|
|
|
|
% We process inst definitions after all type definitions because
|
|
% the processing of a type-specific inst may require access not just
|
|
% to the definition of *that* type, but of any other type that occurs
|
|
% in its definition, either directly or indirectly.
|
|
%
|
|
% Checking insts for circularity cannot be guaranteed to catch all
|
|
% circularities until all insts have been added to the HLDS.
|
|
% For example, in tests/invalid/circ_inst5.m, the circularity between
|
|
% the inst_ctors c/0 and c/1 is detected only if the definition of
|
|
% the inst_ctor i/1 has already been added to the inst table, but
|
|
% i/1 does not refer to either c/0 or c1, and is not itself circular,
|
|
% so its later addition cannot be expected to catch the circularity
|
|
% between c/0 and c/1.
|
|
%
|
|
% We definitely do want to guarantee that we catch and report all
|
|
% circular insts *now*, because later compiler passes *assume* that
|
|
% this has been done, and thus employ recursive algorithms on insts
|
|
% that do not check for infinite recursion. Any circularity we miss here
|
|
% is thus very likely to cause a later pass to enter an infinite loop.
|
|
add_inst_defns(InstDefns,
|
|
!ModuleInfo, !Specs),
|
|
check_inst_defns(!.ModuleInfo, InstDefns,
|
|
!FoundInvalidInstOrMode, !Specs),
|
|
|
|
% Mode definitions may refer to user defined insts. Since we have already
|
|
% seen all inst definitions, we could check whether the newly defined
|
|
% mode refers to an undefined inst, but we do not (yet) do so.
|
|
add_mode_defns(ModeDefns,
|
|
!ModuleInfo, !Specs),
|
|
check_mode_defns(!.ModuleInfo, ModeDefns,
|
|
!FoundInvalidInstOrMode, !Specs),
|
|
|
|
% A predicate declaration defines the type of the arguments of a predicate,
|
|
% and may give the modes of those arguments. Since we have already seen
|
|
% all type, inst and mode definitions, we could check whether the newly
|
|
% defined predicate refers to an undefined type, inst, or mode, but
|
|
% we do not (yet) do so.
|
|
add_pred_decls(PredDecls,
|
|
!ModuleInfo, !Specs),
|
|
add_pred_decls(SolverPredDecls,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% We need to process the mode declaration of a predicate
|
|
% after we have seen the (type) declaration of the predicate.
|
|
% In the past, this required the predicate declaration to precede
|
|
% the mode declaration in the source code, but now, the order
|
|
% in the source code does not matter.
|
|
%
|
|
% Note that mode declarations embedded in predmode declarations
|
|
% have already been added to the HLDS by add_pred_decl.
|
|
add_mode_decls(ModeDecls,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% Every mutable has its own set of access predicates. Declare them
|
|
% (using predmode declarations, which specify their types, modes and
|
|
% determinism) whether the mutable definition was a standalone item
|
|
% or part of the definition of a solver type.
|
|
%
|
|
% We have to do this after we add types and insts to the HLDS,
|
|
% so we can check the types and insts in the mutable for validity.
|
|
% XXX Currently, we check only the inst for validity.
|
|
AllMutables = Mutables ++ SolverMutables,
|
|
module_info_get_user_init_pred_target_names(!.ModuleInfo,
|
|
InitPredTargetNames0),
|
|
implement_mutables_if_local(!.ModuleInfo, AllMutables,
|
|
MutablePredDecls, MutableClauses, MutableForeignProcs,
|
|
MutableForeignDeclCodes, MutableForeignBodyCodes, FPEInfosCord1,
|
|
InitPredTargetNames0, InitPredTargetNames1, !Specs),
|
|
add_pred_decls(MutablePredDecls,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% Record the definitions of typeclasses.
|
|
% This will add the pred and mode declarations of the methods inside them
|
|
% to the HLDS.
|
|
add_typeclass_defns(Typeclasses,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% The old pass 2.
|
|
|
|
% Now that we have processed all mode declarations, we have a reliable
|
|
% record of which declared predicates and functions have NO mode
|
|
% declaration. For functions, this is not an error, since Mercury
|
|
% specifies a implicit mode declaration for them. maybe_add_default_mode
|
|
% adds that implicit mode declaration in such circumstances.
|
|
% XXX It should also generate error messages for PREDICATES that have
|
|
% no mode declaration.
|
|
maybe_add_default_modes(PredDecls,
|
|
!ModuleInfo),
|
|
% The items in MutablePredDecls do not have default modes to add.
|
|
|
|
% Record instance definitions.
|
|
add_instance_defns(Instances,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% Implement several kinds of pragmas, the ones in the subtype
|
|
% defined by the pragma_pass_2 inst.
|
|
|
|
% Record imports of foreign-language modules for use by the target code
|
|
% we may generate.
|
|
list.foldl(module_add_item_fim, FIMs,
|
|
!ModuleInfo),
|
|
|
|
% Since all declared predicates are in now the HLDS, we could check
|
|
% all type definitions that define type-specific unify and/or compare
|
|
% predicates whether the predicate names they give refer to predicates
|
|
% that (a) exist, and (b) have the right signature. However, we currently
|
|
% don't do that. Any such errors are discovered when we type and mode
|
|
% check that automatically created unify and compare predicates, whose
|
|
% bodies call the user-specified predicate names.
|
|
% XXX PASS STRUCTURE Maybe we *should* check that here, since doing so
|
|
% would allow us to generate better error messages.
|
|
(
|
|
!.FoundInvalidType = did_not_find_invalid_type,
|
|
% Add constructors for du types to the HLDS, check subtype definitions,
|
|
% and check that Mercury types defined solely by foreign types have a
|
|
% definition that works for the target backend.
|
|
%
|
|
% This must be done after adding all type definitions and all
|
|
% `:- pragma foreign_type' declarations. If there were errors
|
|
% in foreign type type declarations, doing this may cause
|
|
% a compiler abort.
|
|
module_info_get_type_table(!.ModuleInfo, TypeTable0),
|
|
foldl3_over_type_ctor_defns(
|
|
add_du_ctors_check_subtype_check_foreign_type(TypeTable0),
|
|
TypeTable0, !FoundInvalidType, !ModuleInfo, !Specs)
|
|
;
|
|
!.FoundInvalidType = found_invalid_type
|
|
),
|
|
|
|
% Add the special preds for the builtin types which don't have a
|
|
% type declaration, hence no hlds_type_defn is generated for them.
|
|
( if
|
|
ModuleName = mercury_public_builtin_module,
|
|
compiler_generated_rtti_for_builtins(!.ModuleInfo)
|
|
then
|
|
list.foldl(add_builtin_type_ctor_special_preds_in_builtin_module,
|
|
builtin_type_ctors_with_no_hlds_type_defn, !ModuleInfo)
|
|
else
|
|
true
|
|
),
|
|
|
|
% Balance any data structures that need it.
|
|
module_info_optimize(!ModuleInfo),
|
|
|
|
% The old pass 3.
|
|
init_qual_info(MQInfo0, TypeEqvMap, !:QualInfo),
|
|
|
|
% Add clauses to their predicates.
|
|
add_clauses(Clauses,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
% Add clauses that define the auxiliary predicates
|
|
% that implement some of the auxiliary predicates of mutables.
|
|
add_clauses(MutableClauses,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
% Add clauses that record promises.
|
|
add_promises(Promises,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
|
|
% Remember attempts to define predicates in the interface.
|
|
module_info_set_int_bad_clauses(IntBadClauses, !ModuleInfo),
|
|
|
|
% Add foreign proc definitions
|
|
% - for the auxiliary predicates that implement solver types, and
|
|
% - for the rest of the auxiliary predicates that implement mutables.
|
|
add_pragma_foreign_procs(SolverForeignProcs,
|
|
!ModuleInfo, !Specs),
|
|
add_pragma_foreign_procs(MutableForeignProcs,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% Check that the predicates listed in `:- initialise' and `:- finalise'
|
|
% declarations exist and have the correct signature, construct
|
|
% pragma foreign_export declarations for them, and record their exported
|
|
% names in the module_info, so that we can generate code to call them
|
|
% at initialisation/finalisation time.
|
|
%
|
|
% InitPredTargetNames1 is set by the call to
|
|
% implement_mutables_if_local above.
|
|
module_info_get_user_final_pred_target_names(!.ModuleInfo,
|
|
FinalPredTargetNames1),
|
|
add_initialises(!.ModuleInfo, Initialises,
|
|
FPEInfosCord1, FPEInfosCord2,
|
|
InitPredTargetNames1, InitPredTargetNames, !Specs),
|
|
add_finalises(!.ModuleInfo, Finalises,
|
|
FPEInfosCord2, FPEInfosCord,
|
|
FinalPredTargetNames1, FinalPredTargetNames, !Specs),
|
|
module_info_set_user_init_pred_target_names(InitPredTargetNames,
|
|
!ModuleInfo),
|
|
module_info_set_user_final_pred_target_names(FinalPredTargetNames,
|
|
!ModuleInfo),
|
|
|
|
% Implement all pragmas.
|
|
%
|
|
% Once upon a time, we processed many pragmas in pass 2.
|
|
% We had three different reasons for doing this for three different
|
|
% kinds of pragmas.
|
|
%
|
|
% 1: We had to do this for foreign_enum pragmas, since these may affect
|
|
% type representations, and *that* may affect many other things.
|
|
% However, these now have their own item kind, which are processed
|
|
% at the very top above.
|
|
%
|
|
% 2: We once also had to do this for foreign_decl pragmas, but the
|
|
% separation of user and aux foreign decls has removed that requirement
|
|
% (see the comments on the module_info fields containing foreign_decls
|
|
% in hlds_module.m).
|
|
%
|
|
% 3: We wanted to do this also for pragmas that mark predicates and/or
|
|
% functions as external, so that we could catch and report attempts
|
|
% to add clauses for any predicates and/or functions so marked, However,
|
|
% we have no code to actually do this. If we ever *do* want to implement
|
|
% this check, it can easily be done in this pass: just emit the error
|
|
% message if a predicate or function being marked has some clauses.
|
|
%
|
|
% We can now add all the remaining pragmas to the HLDS at the same time.
|
|
% This times does have to be after we have processed all predicate
|
|
% and mode declarations, since several pragmas do refer to predicates
|
|
% or to modes of predicates.
|
|
add_decl_pragmas(PragmasDecl,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
add_decl_pragmas_type_spec(PragmasDeclTypeSpec,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
add_decl_pragmas_term_info(PragmasDeclTermInfo,
|
|
!ModuleInfo, !Specs),
|
|
add_decl_pragmas_term2_info(PragmasDeclTerm2Info,
|
|
!ModuleInfo, !Specs),
|
|
add_decl_pragmas_sharing(PragmasDeclSharing,
|
|
!ModuleInfo, !Specs),
|
|
add_decl_pragmas_reuse(PragmasDeclReuse,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% We want to process tabled pragmas *after* any inline pragmas
|
|
% (which are also impl pragmas), so that we can detect and report
|
|
% the problem if a predicate or function has both an inline pragma
|
|
% and a tabled pragma.
|
|
add_impl_pragmas(PragmasImpl, cord.init, PragmaTabledCord,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
PragmasTabled = cord.list(PragmaTabledCord),
|
|
add_impl_pragmas_tabled(PragmasTabled,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
|
|
FPEInfos = cord.list(FPEInfosCord),
|
|
list.foldl2(add_pragma_info_foreign_proc_export, FPEInfos,
|
|
!ModuleInfo, !Specs),
|
|
|
|
list.foldl(module_add_foreign_decl_code_aux, MutableForeignDeclCodes,
|
|
!ModuleInfo),
|
|
list.foldl(module_add_foreign_body_code, MutableForeignBodyCodes,
|
|
!ModuleInfo),
|
|
|
|
list.foldl2(add_gen_pragma_unused_args, PragmasGenUnusedArgs,
|
|
!ModuleInfo, !Specs),
|
|
list.foldl2(add_gen_pragma_exceptions, PragmasGenExceptions,
|
|
!ModuleInfo, !Specs),
|
|
list.foldl2(add_gen_pragma_trailing, PragmasGenTrailing,
|
|
!ModuleInfo, !Specs),
|
|
list.foldl2(add_gen_pragma_mm_tabling, PragmasGenMMTabling,
|
|
!ModuleInfo, !Specs),
|
|
|
|
% Check that the declarations for field extraction and update functions
|
|
% are sensible, and generate error messages for the ones that aren't.
|
|
% We can do this only after we have processed every predicate declaration,
|
|
% as well as everything that affects either the type table or the
|
|
% constructor table.
|
|
check_preds_if_field_access_function(!.ModuleInfo, PredDecls, !Specs),
|
|
|
|
ModuleItemVersionNumbers =
|
|
AugCompUnit ^ acu_module_item_version_numbers_map,
|
|
map.foldl(add_module_item_version_numbers, ModuleItemVersionNumbers,
|
|
!QualInfo),
|
|
|
|
qual_info_get_mq_info(!.QualInfo, MQInfo),
|
|
mq_info_get_found_undef_type(MQInfo, MQUndefType),
|
|
mq_info_get_found_undef_inst(MQInfo, MQUndefInst),
|
|
mq_info_get_found_undef_mode(MQInfo, MQUndefMode),
|
|
mq_info_get_found_undef_typeclass(MQInfo, MQUndefTypeClass),
|
|
( if
|
|
( MQUndefType = found_undef_type
|
|
; MQUndefTypeClass = found_undef_typeclass
|
|
)
|
|
then
|
|
!:FoundInvalidType = found_invalid_type
|
|
else
|
|
true
|
|
),
|
|
( if
|
|
( MQUndefInst = found_undef_inst
|
|
; MQUndefMode = found_undef_mode
|
|
)
|
|
then
|
|
!:FoundInvalidInstOrMode = found_invalid_inst_or_mode
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_builtin_type_ctor_special_preds_in_builtin_module(type_ctor::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
add_builtin_type_ctor_special_preds_in_builtin_module(TypeCtor, !ModuleInfo) :-
|
|
varset.init(TVarSet),
|
|
% These predicates are local only in the public builtin module,
|
|
% but we *get here* only if we are compiling the public builtin module.
|
|
TypeStatus = type_status(status_local),
|
|
construct_type(TypeCtor, [], Type),
|
|
% You cannot construct clauses to unify or compare values of an abstract
|
|
% type. The abstract body tells the callee to generate code for
|
|
% a builtin type.
|
|
Body = hlds_abstract_type(abstract_type_general),
|
|
% XXX For some reason, when generating C# code, if we don't add
|
|
% the special preds now, we don't add them ever, which can cause compiler
|
|
% aborts when we try to generate the type's type_ctor_info structure
|
|
% (since we need to put references to the type's unify and compare
|
|
% predicates into that structure.)
|
|
add_special_pred_decl_defns_for_type_eagerly(TVarSet,
|
|
Type, TypeCtor, Body, TypeStatus, dummy_context, !ModuleInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_item_avails(ims_list(item_avail)::in,
|
|
set(module_name)::in, set(module_name)::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
add_item_avails([], !AncestorAvailModules, !ModuleInfo).
|
|
add_item_avails([ImsList | ImsLists], !AncestorAvailModules, !ModuleInfo) :-
|
|
ImsList = ims_sub_list(ItemMercuryStatus, Avails),
|
|
list.foldl2(add_item_avail(ItemMercuryStatus), Avails,
|
|
!AncestorAvailModules, !ModuleInfo),
|
|
add_item_avails(ImsLists, !AncestorAvailModules, !ModuleInfo).
|
|
|
|
:- pred add_item_avail(item_mercury_status::in, item_avail::in,
|
|
set(module_name)::in, set(module_name)::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
add_item_avail(ItemMercuryStatus, Avail, !AncestorAvailModules, !ModuleInfo) :-
|
|
(
|
|
Avail = avail_import(avail_import_info(ModuleName, Context, _SeqNum)),
|
|
ImportOrUse = import_decl
|
|
;
|
|
Avail = avail_use(avail_use_info(ModuleName, Context, _SeqNum)),
|
|
ImportOrUse = use_decl
|
|
),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(ItemExport),
|
|
(
|
|
ItemExport = item_export_anywhere,
|
|
Section = ms_interface
|
|
;
|
|
( ItemExport = item_export_nowhere
|
|
; ItemExport = item_export_only_submodules
|
|
),
|
|
Section = ms_implementation
|
|
),
|
|
module_add_avail_module_name(ModuleName, Section, ImportOrUse,
|
|
yes(Context), !ModuleInfo)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(ItemImport),
|
|
(
|
|
ItemImport = item_import_int_concrete(ImportLocn),
|
|
(
|
|
(
|
|
ImportLocn = import_locn_ancestor_int0_interface,
|
|
Section = ms_interface
|
|
;
|
|
ImportLocn = import_locn_ancestor_int0_implementation,
|
|
Section = ms_implementation
|
|
),
|
|
module_add_avail_module_name(ModuleName, Section,
|
|
ImportOrUse, no, !ModuleInfo),
|
|
set.insert(ModuleName, !AncestorAvailModules)
|
|
;
|
|
( ImportLocn = import_locn_interface
|
|
; ImportLocn = import_locn_implementation
|
|
; ImportLocn = import_locn_import_by_ancestor
|
|
),
|
|
% XXX Given that we get here only if ItemMercuryStatus says
|
|
% the item is from another module, the import cannot be
|
|
% in either the interface or the implementation section
|
|
% of *this* module, so neither import_locn_interface
|
|
% nor import_locn_implementation should be possible
|
|
% without misuse of those import_locns.
|
|
module_add_indirectly_imported_module_name(ModuleName,
|
|
!ModuleInfo)
|
|
)
|
|
;
|
|
( ItemImport = item_import_int_abstract
|
|
; ItemImport = item_import_opt_int
|
|
),
|
|
module_add_indirectly_imported_module_name(ModuleName, !ModuleInfo)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_type_defns(sec_list(item_type_defn_info)::in,
|
|
module_info::in, module_info::out,
|
|
found_invalid_type::in, found_invalid_type::out,
|
|
list(error_spec)::in, list(error_spec)::out,
|
|
sec_cord(item_pred_decl_info)::in, sec_cord(item_pred_decl_info)::out,
|
|
ims_cord(item_foreign_proc)::in, ims_cord(item_foreign_proc)::out,
|
|
sec_cord(item_mutable_info)::in, sec_cord(item_mutable_info)::out) is det.
|
|
|
|
add_type_defns([], !ModuleInfo, !FoundInvalidType,
|
|
!Specs, !PredDeclCord, !ForeignProcCord, !MutableCord).
|
|
add_type_defns([SecList | SecLists], !ModuleInfo, !FoundInvalidType,
|
|
!Specs, !PredDeclCord, !ForeignProcCord, !MutableCord) :-
|
|
SecList = sec_sub_list(SectionInfo, TypeDefns),
|
|
SectionInfo = sec_info(ItemMercuryStatus, _NeedQual),
|
|
item_mercury_status_to_type_status(ItemMercuryStatus, TypeStatus),
|
|
list.foldl6(add_type_defn(SectionInfo, TypeStatus), TypeDefns,
|
|
!ModuleInfo, !FoundInvalidType,
|
|
!Specs, !PredDeclCord, !ForeignProcCord, !MutableCord),
|
|
add_type_defns(SecLists, !ModuleInfo, !FoundInvalidType,
|
|
!Specs, !PredDeclCord, !ForeignProcCord, !MutableCord).
|
|
|
|
:- pred add_type_defn(sec_info::in, type_status::in, item_type_defn_info::in,
|
|
module_info::in, module_info::out,
|
|
found_invalid_type::in, found_invalid_type::out,
|
|
list(error_spec)::in, list(error_spec)::out,
|
|
sec_cord(item_pred_decl_info)::in, sec_cord(item_pred_decl_info)::out,
|
|
ims_cord(item_foreign_proc)::in, ims_cord(item_foreign_proc)::out,
|
|
sec_cord(item_mutable_info)::in, sec_cord(item_mutable_info)::out) is det.
|
|
|
|
add_type_defn(SectionInfo, TypeStatus, TypeDefnInfo,
|
|
!ModuleInfo, !FoundInvalidType, !Specs,
|
|
!PredDeclCord, !ForeignProcCord, !MutableCord) :-
|
|
SectionInfo = sec_info(ItemMercuryStatus, NeedQual),
|
|
TypeDefnInfo = item_type_defn_info(SymName, TypeParams, TypeDefn,
|
|
TypeVarSet, Context, _SeqNum),
|
|
(
|
|
TypeDefn = parse_tree_solver_type(Detailssolver),
|
|
Detailssolver =
|
|
type_details_solver(SolverTypeDetails, _MaybeUserEqComp),
|
|
% If this is a solver type, then we need to also declare and define
|
|
% the compiler generated construction function and deconstruction
|
|
% predicate for the special constrained data constructor.
|
|
% Before switch detection, we turn calls to these functions/predicates
|
|
% into ordinary constructions/deconstructions, but preserve the
|
|
% corresponding impurity annotations.
|
|
%
|
|
% Our caller will add !:PredDeclCord, !:ForeignProcCord and
|
|
% !:MutableCord to the HLDS (roughly) when it processes other
|
|
% predicate declarations, foreign procs and mutables.
|
|
SolverAuxPredInfo = solver_aux_pred_info(SymName, TypeParams,
|
|
TypeVarSet, SolverTypeDetails, Context),
|
|
get_solver_type_aux_pred_decls(SolverAuxPredInfo, PredDeclInfos),
|
|
PredDeclList = sec_sub_list(SectionInfo, PredDeclInfos),
|
|
cord.snoc(PredDeclList, !PredDeclCord),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(_),
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
get_solver_type_aux_pred_defns(Target, SolverAuxPredInfo,
|
|
ForeignProcInfos),
|
|
ForeignProcList =
|
|
ims_sub_list(ItemMercuryStatus, ForeignProcInfos),
|
|
cord.snoc(ForeignProcList, !ForeignProcCord),
|
|
|
|
MutableItems = SolverTypeDetails ^ std_mutable_items,
|
|
MutableList = sec_sub_list(SectionInfo, MutableItems),
|
|
cord.snoc(MutableList, !MutableCord)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(_)
|
|
)
|
|
;
|
|
( TypeDefn = parse_tree_du_type(_)
|
|
; TypeDefn = parse_tree_sub_type(_)
|
|
; TypeDefn = parse_tree_eqv_type(_)
|
|
; TypeDefn = parse_tree_abstract_type(_)
|
|
; TypeDefn = parse_tree_foreign_type(_)
|
|
)
|
|
),
|
|
module_add_type_defn(TypeStatus, NeedQual, TypeDefnInfo,
|
|
!ModuleInfo, !FoundInvalidType, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_inst_defns(ims_list(item_inst_defn_info)::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_inst_defns([], !ModuleInfo, !Specs).
|
|
add_inst_defns([ImsSubList | ImsSubLists], !ModuleInfo, !Specs) :-
|
|
ImsSubList = ims_sub_list(ItemMercuryStatus, InstDefns),
|
|
item_mercury_status_to_inst_status(ItemMercuryStatus, InstStatus),
|
|
list.foldl2(module_add_inst_defn(InstStatus), InstDefns,
|
|
!ModuleInfo, !Specs),
|
|
add_inst_defns(ImsSubLists, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_mode_defns(ims_list(item_mode_defn_info)::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mode_defns([], !ModuleInfo, !Specs).
|
|
add_mode_defns([ImsSubList | ImsSubLists], !ModuleInfo, !Specs) :-
|
|
ImsSubList = ims_sub_list(ItemMercuryStatus, ModeDefns),
|
|
item_mercury_status_to_mode_status(ItemMercuryStatus, ModeStatus),
|
|
list.foldl2(module_add_mode_defn(ModeStatus), ModeDefns,
|
|
!ModuleInfo, !Specs),
|
|
add_mode_defns(ImsSubLists, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_pred_decls(sec_list(item_pred_decl_info)::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_pred_decls([], !ModuleInfo, !Specs).
|
|
add_pred_decls([SecSubList | SecSubLists], !ModuleInfo, !Specs) :-
|
|
SecSubList = sec_sub_list(SectionInfo, PredDecls),
|
|
SectionInfo = sec_info(ItemMercuryStatus, NeedQual),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
list.map_foldl2(
|
|
module_add_pred_decl(ItemMercuryStatus, PredStatus, NeedQual),
|
|
PredDecls, _MaybePredProcIds, !ModuleInfo, !Specs),
|
|
add_pred_decls(SecSubLists, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_mode_decls(ims_list(item_mode_decl_info)::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mode_decls([], !ModuleInfo, !Specs).
|
|
add_mode_decls([SecSubList | SecSubLists], !ModuleInfo, !Specs) :-
|
|
SecSubList = ims_sub_list(ItemMercuryStatus, ModeDecls),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
list.map_foldl2(
|
|
module_add_mode_decl(not_part_of_predmode, is_not_a_class_method,
|
|
ItemMercuryStatus, PredStatus),
|
|
ModeDecls, _PredProcIds, !ModuleInfo, !Specs),
|
|
add_mode_decls(SecSubLists, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_add_default_modes(sec_list(item_pred_decl_info)::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
maybe_add_default_modes([], !ModuleInfo).
|
|
maybe_add_default_modes([SecSubList | SecSubLists], !ModuleInfo) :-
|
|
SecSubList = sec_sub_list(_SectionInfo, Items),
|
|
list.foldl(maybe_add_default_mode, Items, !ModuleInfo),
|
|
maybe_add_default_modes(SecSubLists, !ModuleInfo).
|
|
|
|
:- pred maybe_add_default_mode(item_pred_decl_info::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
maybe_add_default_mode(PredDecl, !ModuleInfo) :-
|
|
PredDecl = item_pred_decl_info(PredSymName, PredOrFunc, TypesAndModes,
|
|
_, _, _, _, _, _, _, _, _, _, _),
|
|
% Add default modes for function declarations, if necessary.
|
|
PredName = unqualify_name(PredSymName),
|
|
( if PredName = "" then
|
|
% We didn't add the predicate declaration itself above,
|
|
% so we cannot possibly add anything to it now.
|
|
true
|
|
else
|
|
(
|
|
PredOrFunc = pf_predicate
|
|
;
|
|
PredOrFunc = pf_function,
|
|
PredFormArity = arg_list_arity(TypesAndModes),
|
|
user_arity_pred_form_arity(PredOrFunc, UserArity, PredFormArity),
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
predicate_table_lookup_func_sym_arity(PredTable0,
|
|
is_fully_qualified, PredSymName, UserArity, PredIds),
|
|
(
|
|
PredIds = [_ | _],
|
|
predicate_table_get_pred_id_table(PredTable0, PredIdTable0),
|
|
maybe_add_default_func_modes(!.ModuleInfo, PredIds,
|
|
PredIdTable0, PredIdTable),
|
|
predicate_table_set_pred_id_table(PredIdTable,
|
|
PredTable0, PredTable),
|
|
module_info_set_predicate_table(PredTable, !ModuleInfo)
|
|
;
|
|
PredIds = [],
|
|
unexpected($pred, "can't find func declaration")
|
|
)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_clauses(ims_list(item_clause_info)::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_clauses([], !ModuleInfo, !QualInfo, !Specs).
|
|
add_clauses([ImsList | ImsLists], !ModuleInfo, !QualInfo, !Specs) :-
|
|
ImsList = ims_sub_list(ItemMercuryStatus, Items),
|
|
ClauseType = clause_not_for_promise,
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
list.foldl3(module_add_clause(PredStatus, ClauseType), Items,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
add_clauses(ImsLists, !ModuleInfo, !QualInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_promises(ims_list(item_promise_info)::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_promises([], !ModuleInfo, !QualInfo, !Specs).
|
|
add_promises([ImsList | ImsLists], !ModuleInfo, !QualInfo, !Specs) :-
|
|
ImsList = ims_sub_list(ItemMercuryStatus, Items),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
list.foldl3(add_promise(PredStatus), Items,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
add_promises(ImsLists, !ModuleInfo, !QualInfo, !Specs).
|
|
|
|
:- pred add_promise(pred_status::in, item_promise_info::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_promise(PredStatus, PromiseInfo, !ModuleInfo, !QualInfo, !Specs) :-
|
|
PromiseInfo = item_promise_info(PromiseType, Goal, VarSet, UnivVars,
|
|
Context, SeqNum),
|
|
% Promise declarations are recorded as a predicate with a goal_type
|
|
% of goal_type_promise(X), where X is a promise_type. This allows us
|
|
% to leverage off all the other checks in the compiler that operate
|
|
% on predicates.
|
|
%
|
|
% :- promise all [A,B,R] ( R = A + B <=> R = B + A ).
|
|
%
|
|
% becomes
|
|
%
|
|
% promise__lineno__filename(A, B, R) :-
|
|
% ( R = A + B <=> R = B + A ).
|
|
%
|
|
% The double underscores in the compiler-generated name would be turned
|
|
% into module qualifications if the name were provided by the user,
|
|
% guarding against accidental name clashes.
|
|
%
|
|
% XXX We should be testing Goal here to see if it has the form of
|
|
% one of the supported kinds of promises, recording it in a
|
|
% promise-kind-specific format if it is, and rejecting it with warning
|
|
% if it is not.
|
|
(
|
|
( PromiseType = promise_type_exclusive
|
|
; PromiseType = promise_type_exhaustive
|
|
; PromiseType = promise_type_exclusive_exhaustive
|
|
),
|
|
% Extra error checking for promise ex declarations.
|
|
check_promise_ex_decl(UnivVars, PromiseType, Goal, Context, !Specs)
|
|
;
|
|
PromiseType = promise_type_true
|
|
),
|
|
GoalType = goal_for_promise(PromiseType),
|
|
ClauseType = clause_for_promise(PromiseType),
|
|
|
|
FileName = term_context.context_file(Context),
|
|
LineNumber = term_context.context_line(Context),
|
|
PromisePredName = promise_pred_name(PromiseType, FileName, LineNumber),
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
PromiseModuleName = ModuleName,
|
|
PromisePredSymName = qualified(PromiseModuleName, PromisePredName),
|
|
|
|
% If the outermost universally quantified variables are placed in the head
|
|
% of the dummy predicate, the typechecker will avoid warning about unbound
|
|
% type variables, as this implicitly adds a universal quantification of the
|
|
% type variables needed.
|
|
HeadVars = UnivVars,
|
|
PredFormArity = arg_list_arity(HeadVars),
|
|
clauses_info_init_for_assertion(HeadVars, ClausesInfo),
|
|
PredOrigin = origin_user(
|
|
user_made_assertion(PromiseType, FileName, LineNumber)),
|
|
add_implicit_pred_decl(pf_predicate, PromiseModuleName, PromisePredName,
|
|
PredFormArity, PredStatus, Context, PredOrigin, GoalType, ClausesInfo,
|
|
_PredId, !ModuleInfo),
|
|
|
|
term_subst.var_list_to_term_list(HeadVars, HeadVarTerms),
|
|
ClauseInfo = item_clause_info(pf_predicate, PromisePredSymName,
|
|
HeadVarTerms, VarSet, ok2(Goal, []), Context, SeqNum),
|
|
module_add_clause(PredStatus, ClauseType, ClauseInfo,
|
|
!ModuleInfo, !QualInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_initialises(module_info::in, ims_list(item_initialise_info)::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::out,
|
|
pred_target_names::in, pred_target_names::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_initialises(_, [], !PragmaFPEInfoCord, !PredTargetNames, !Specs).
|
|
add_initialises(ModuleInfo, [ImsList | ImsLists],
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs) :-
|
|
ImsList = ims_sub_list(ItemMercuryStatus, Items),
|
|
list.foldl3(add_initialise(ModuleInfo, ItemMercuryStatus),
|
|
Items, !PragmaFPEInfoCord, !PredTargetNames, !Specs),
|
|
add_initialises(ModuleInfo, ImsLists,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs).
|
|
|
|
:- pred add_initialise(module_info::in, item_mercury_status::in,
|
|
item_initialise_info::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::out,
|
|
pred_target_names::in, pred_target_names::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_initialise(ModuleInfo, ItemMercuryStatus, Initialise,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs) :-
|
|
Initialise = item_initialise_info(SymName, Arity, Origin, Context, SeqNum),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(_),
|
|
(
|
|
Origin = item_origin_user,
|
|
implement_initialise_finalise(ModuleInfo, iof_init,
|
|
SymName, Arity, Context, SeqNum,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs)
|
|
;
|
|
Origin = item_origin_compiler(_CompilerAttrs),
|
|
unexpected($pred, "bad introduced initialise declaration")
|
|
)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(_)
|
|
% It is OK if the initialise is defined in a parent module,
|
|
% and we get to see it (though it SHOULD be kept out of .int0 files),
|
|
% but we should NOT implement it.
|
|
).
|
|
|
|
:- pred add_finalises(module_info::in, ims_list(item_finalise_info)::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::out,
|
|
pred_target_names::in, pred_target_names::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_finalises(_, [], !PragmaFPEInfoCord, !PredTargetNames, !Specs).
|
|
add_finalises(ModuleInfo, [ImsList | ImsLists],
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs) :-
|
|
ImsList = ims_sub_list(ItemMercuryStatus, Items),
|
|
list.foldl3(add_finalise(ModuleInfo, ItemMercuryStatus),
|
|
Items, !PragmaFPEInfoCord, !PredTargetNames, !Specs),
|
|
add_finalises(ModuleInfo, ImsLists,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs).
|
|
|
|
:- pred add_finalise(module_info::in, item_mercury_status::in,
|
|
item_finalise_info::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::out,
|
|
pred_target_names::in, pred_target_names::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_finalise(ModuleInfo, ItemMercuryStatus, FinaliseInfo,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs) :-
|
|
FinaliseInfo = item_finalise_info(SymName, Arity, Origin, Context, SeqNum),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(_),
|
|
(
|
|
Origin = item_origin_user,
|
|
implement_initialise_finalise(ModuleInfo, iof_final,
|
|
SymName, Arity, Context, SeqNum,
|
|
!PragmaFPEInfoCord, !PredTargetNames, !Specs)
|
|
;
|
|
Origin = item_origin_compiler(_),
|
|
unexpected($pred, "bad introduced finalise declaration")
|
|
)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(_)
|
|
% It is OK if the initialise is defined in a parent module,
|
|
% and we get to see it (though it SHOULD be kept out of .int0 files),
|
|
% but we should NOT implement it.
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type init_or_final
|
|
---> iof_init
|
|
; iof_final.
|
|
|
|
:- pred implement_initialise_finalise(module_info::in, init_or_final::in,
|
|
sym_name::in, user_arity::in, prog_context::in, item_seq_num::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::in,
|
|
cord(item_pragma_info(pragma_info_foreign_proc_export))::out,
|
|
pred_target_names::in, pred_target_names::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
implement_initialise_finalise(ModuleInfo, InitOrFinal, SymName, UserArity,
|
|
Context, SeqNum, !PragmaFPEInfoCord, !PredTargetNames, !Specs) :-
|
|
% To implement an `:- initialise pred.' or `:- finalise pred' declaration
|
|
% for C backends, we need to:
|
|
%
|
|
% (1) construct a new C function name, CName, to use to export pred,
|
|
% (2) add the export pragma that does this,
|
|
% (3) record the pred/cname pair in the ModuleInfo so that
|
|
% code generation can ensure CName is called during module
|
|
% initialisation/finalisation
|
|
|
|
module_info_get_predicate_table(ModuleInfo, PredTable),
|
|
UserArity = user_arity(UserArityInt),
|
|
predicate_table_lookup_pred_sym_arity(PredTable,
|
|
may_be_partially_qualified, SymName, UserArity, PredIds),
|
|
( InitOrFinal = iof_init, DeclName = "initialise"
|
|
; InitOrFinal = iof_final, DeclName = "finalise"
|
|
),
|
|
(
|
|
PredIds = [],
|
|
Pieces = [words("Error:"),
|
|
qual_sym_name_arity(sym_name_arity(SymName, UserArityInt)),
|
|
words("used in"), decl(DeclName), words("declaration"),
|
|
words("does not have a corresponding"),
|
|
decl("pred"), words("declaration."), nl],
|
|
Spec = simplest_spec($pred, severity_error, phase_parse_tree_to_hlds,
|
|
Context, Pieces),
|
|
!:Specs = [Spec | !.Specs]
|
|
;
|
|
PredIds = [PredId],
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
( if is_valid_init_or_final_pred(PredInfo, ExpectedHeadModes) then
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
(
|
|
InitOrFinal = iof_init,
|
|
NameIoF = "init",
|
|
Origin = compiler_origin_initialise
|
|
;
|
|
InitOrFinal = iof_final,
|
|
NameIoF = "final",
|
|
Origin = compiler_origin_finalise
|
|
),
|
|
new_user_init_or_final_pred_target_name(ModuleName, NameIoF,
|
|
SeqNum, SymName, UserArity, TargetName, !PredTargetNames),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
make_pragma_foreign_proc_export(Globals, SymName,
|
|
ExpectedHeadModes, TargetName, Origin, Context, PragmaFPEInfo),
|
|
cord.snoc(PragmaFPEInfo, !PragmaFPEInfoCord)
|
|
else
|
|
Pieces = [words("Error:"),
|
|
qual_sym_name_arity(sym_name_arity(SymName, UserArityInt)),
|
|
words("used in"), decl(DeclName), words("declaration"),
|
|
words("has an invalid signature."), nl,
|
|
words("A signature is valid only if it has"),
|
|
words("one of these two forms:"),
|
|
nl_indent_delta(1),
|
|
quote(":- pred <predname>(io::di, io::uo) is <detism>."), nl,
|
|
quote(":- impure pred <predname> is <detism>."),
|
|
nl_indent_delta(-1),
|
|
words("where"), quote("<detism>"), words("is either"),
|
|
quote("det"), words("or"), quote("cc_multi"), suffix("."), nl],
|
|
Spec = simplest_spec($pred, severity_error,
|
|
phase_parse_tree_to_hlds, Context, Pieces),
|
|
!:Specs = [Spec | !.Specs]
|
|
)
|
|
;
|
|
PredIds = [_, _ | _],
|
|
Pieces = [words("Error:"),
|
|
qual_sym_name_arity(sym_name_arity(SymName, UserArityInt)),
|
|
words("used in"), decl(DeclName), words("declaration"),
|
|
words("has multiple"), decl("pred"), words("declarations."), nl],
|
|
Spec = simplest_spec($pred, severity_error, phase_parse_tree_to_hlds,
|
|
Context, Pieces),
|
|
!:Specs = [Spec | !.Specs]
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred is_valid_init_or_final_pred(pred_info::in, list(mer_mode)::out)
|
|
is semidet.
|
|
|
|
is_valid_init_or_final_pred(PredInfo, ExpectedHeadModes) :-
|
|
pred_info_get_arg_types(PredInfo, ArgTypes),
|
|
pred_info_get_proc_table(PredInfo, ProcTable),
|
|
ProcInfos = map.values(ProcTable),
|
|
(
|
|
ArgTypes = [Arg1Type, Arg2Type],
|
|
type_is_io_state(Arg1Type),
|
|
type_is_io_state(Arg2Type),
|
|
ExpectedHeadModes = [di_mode, uo_mode],
|
|
ExpectedPurity = purity_pure
|
|
;
|
|
ArgTypes = [],
|
|
ExpectedHeadModes = [],
|
|
ExpectedPurity = purity_impure
|
|
),
|
|
some [ProcInfo] (
|
|
list.member(ProcInfo, ProcInfos),
|
|
proc_info_get_maybe_declared_argmodes(ProcInfo, MaybeHeadModes),
|
|
MaybeHeadModes = yes(HeadModes),
|
|
HeadModes = ExpectedHeadModes,
|
|
proc_info_get_declared_determinism(ProcInfo, MaybeDetism),
|
|
MaybeDetism = yes(Detism),
|
|
( Detism = detism_det ; Detism = detism_cc_multi )
|
|
),
|
|
pred_info_get_purity(PredInfo, Purity),
|
|
Purity = ExpectedPurity.
|
|
|
|
:- pred make_pragma_foreign_proc_export(globals::in, sym_name::in,
|
|
list(mer_mode)::in, string::in, compiler_origin::in, prog_context::in,
|
|
item_pragma_info(pragma_info_foreign_proc_export)::out) is det.
|
|
|
|
make_pragma_foreign_proc_export(Globals, SymName, HeadModes, CName,
|
|
Origin, Context, PragmaFPEInfo) :-
|
|
Attrs = item_compiler_attributes(Origin),
|
|
PEOrigin = item_origin_compiler(Attrs),
|
|
globals.get_target(Globals, CompilationTarget),
|
|
ExportLang = target_lang_to_foreign_export_lang(CompilationTarget),
|
|
PredNameModesPF = proc_pf_name_modes(pf_predicate, SymName, HeadModes),
|
|
% Since the pragma is not coming from the user, we won't be
|
|
% generating any error messages for it, which means that
|
|
% the varset won't be used.
|
|
varset.init(VarSet),
|
|
FPEInfo = pragma_info_foreign_proc_export(PEOrigin, ExportLang,
|
|
PredNameModesPF, CName, VarSet),
|
|
PragmaFPEInfo = item_pragma_info(FPEInfo, Context, item_no_seq_num).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_module_item_version_numbers(module_name::in,
|
|
module_item_version_numbers::in, qual_info::in, qual_info::out) is det.
|
|
|
|
add_module_item_version_numbers(ModuleName, ModuleItemVersionNumbers,
|
|
!QualInfo) :-
|
|
% Record the version numbers for each imported module
|
|
% if smart recompilation is enabled.
|
|
apply_to_recompilation_info(
|
|
update_module_item_version_numbers(ModuleName,
|
|
ModuleItemVersionNumbers),
|
|
!QualInfo).
|
|
|
|
:- pred update_module_item_version_numbers(module_name::in,
|
|
module_item_version_numbers::in,
|
|
recompilation_info::in, recompilation_info::out) is det.
|
|
|
|
update_module_item_version_numbers(ModuleName, ModuleItemVersionNumbers,
|
|
!RecompInfo) :-
|
|
VersionNumbersMap0 = !.RecompInfo ^ recomp_version_numbers,
|
|
map.set(ModuleName, ModuleItemVersionNumbers,
|
|
VersionNumbersMap0, VersionNumbersMap),
|
|
!RecompInfo ^ recomp_version_numbers := VersionNumbersMap.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.make_hlds.make_hlds_passes.
|
|
%---------------------------------------------------------------------------%
|