Files
mercury/compiler/module_qual.m
Zoltan Somogyi 6d43c71948 Implement semantic checks for oisu (order independent state update) pragmas.
Estimated hours taken: 30
Branches: main

Implement semantic checks for oisu (order independent state update) pragmas.

compiler/hlds_pred.m:
	Record for each procedure whether it implements an operation
	on a oisu type, and if yes, what kind of operation.

compiler/hlds_module.m:
	Add to module_infos a data structure that lists the oisu pragmas
	in the module, and the procedures mentioned in them. These are intended
	to be used later during code generation.

compiler/add_pragma.m:
	Add such pragmas to the HLDS, after checking whatever properties
	can be checked during the creation of the HLDS.

compiler/oisu_check.m:
	A new module, whose job it is to check those aspects of oisu pragmas
	that can be checked only after other semantics are complete on the
	module.

compiler/check_hlds.m:
	Add the new module.

compiler/notes/compiler_design.html:
	Document the new module.

compiler/mercury_compile_front_end.m:
	Invoke the new module.

compiler/error_util.m:
	Add the new semantic check as a phase.

compiler/mercury_to_mercury.m:
	Fix typos in the code for writing out oisu pragmas.

compiler/prog_io_pragmas.m:
	Fix typos in the code for reading in oisu pragmas.

compiler/module_qual.m:
	Improve the error messages generated for any problems discovered during
	module qualification inside pragmas, by writing out what *kind* of
	pragma the problem was discovered in.

compiler/modules.m:
	Fix a bug: oisu pragmas *can* appear in module interfaces.

compiler/stratify.m:
	Give a predicate a better interface and a name.

compiler/hlds_goal.m:
	Remove a duplicate comment.

compiler/make_hlds_passes.m:
	Fix formatting.

tests/hard_coded/oisu_check_main.{m,exp}:
tests/hard_coded/oisu_check_db.m:
	A new multimodule test case, which uses oisu pragmas correctly.

tests/invalid/oisu_check_add_pragma_errors.{m,err_exp}:
	A new test case, which tests add_pragma.m's ability to diagnose
	the problems it is supposed to diagnose.

tests/invalid/oisu_check_semantic_errors.{m,err_exp}:
	A new test case, which tests oisu_check.m's ability to diagnose
	the problems it is supposed to diagnose.

tests/hard_coded/Mmakefile:
tests/invalid/Mmakefile:
	Enable the new test cases.
2012-10-08 04:14:49 +00:00

2443 lines
98 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1996-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.
%-----------------------------------------------------------------------------%
%
% File: module_qual.m.
% Main authors: stayl, fjh.
%
% Module qualifies types, insts and modes within declaration items.
% The head of all declarations should be module qualified in prog_io.m.
% This module qualifies the bodies of the declarations. Checks for
% undefined types, insts and modes. Uses two passes over the item list,
% one to collect all type, mode and inst ids and a second to do the
% qualification and report errors. If the --warn-interface-imports
% option is set, warns about modules imported in the interface that do
% not need to be in the interface. The modes of lambda expressions are
% qualified in modes.m.
%
%-----------------------------------------------------------------------------%
:- module parse_tree.module_qual.
:- interface.
:- import_module libs.globals.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_item.
:- import_module recompilation.
:- import_module bool.
:- import_module list.
:- import_module maybe.
%-----------------------------------------------------------------------------%
% module_qualify_items(Items0, Items, EventSpecMap0, EventSpecMap,
% Globals, ModuleName, MaybeFileName, EventSpecFileName, MQ_Info,
% UndefTypes, UndefModes, !Specs, !IO):
%
% Items is Items0 with all items module qualified as much as possible;
% likewise for EventSpecMap0 and EventSpecMap.
%
% If MaybeFileName is `yes(FileName)', then report undefined types, insts
% and modes in Items0. MaybeFileName should be `no' when module qualifying
% the short interface.
%
% Errors in EventSpecMap0 will be reported as being for EventSpecFileName.
%
:- pred module_qualify_items(list(item)::in, list(item)::out,
event_spec_map::in, event_spec_map::out, globals::in,
module_name::in, maybe(string)::in, string::in, mq_info::out,
bool::out, bool::out, list(error_spec)::in, list(error_spec)::out) is det.
% This is called from make_hlds to qualify the mode of a lambda expression.
%
:- pred qualify_lambda_mode_list(list(mer_mode)::in,
list(mer_mode)::out, prog_context::in, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
% This is called from make_hlds.m to qualify the modes in a
% clause mode annotation.
%
:- pred qualify_clause_mode_list(list(mer_mode)::in,
list(mer_mode)::out, prog_context::in, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
% This is called from make_hlds to qualify an explicit type qualification.
%
:- pred qualify_type_qualification(mer_type::in, mer_type::out,
prog_context::in, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
% The type mq_info holds information needed for doing module qualification.
%
:- type mq_info.
:- pred mq_info_get_type_error_flag(mq_info::in, bool::out) is det.
:- pred mq_info_get_mode_error_flag(mq_info::in, bool::out) is det.
:- pred mq_info_get_need_qual_flag(mq_info::in, need_qualifier::out) is det.
:- pred mq_info_get_partial_qualifier_info(mq_info::in,
partial_qualifier_info::out) is det.
:- pred mq_info_get_recompilation_info(mq_info::in,
maybe(recompilation_info)::out) is det.
:- pred mq_info_set_need_qual_flag(need_qualifier::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_recompilation_info(maybe(recompilation_info)::in,
mq_info::in, mq_info::out) is det.
% The type partial_qualifier_info holds info need for computing which
% partial quantifiers are visible -- see get_partial_qualifiers/3.
%
:- type partial_qualifier_info.
% Suppose we are processing a definition which defines the symbol
% foo.bar.baz.quux/1. Then we insert the following symbols
% into the symbol table:
% - if the current value of the NeedQual flag at this point
% is `may_be_unqualified',
% i.e. module `foo.bar.baz' was imported
% then we insert the fully unqualified symbol quux/1;
% - if module `foo.bar.baz' occurs in the "imported" section,
% i.e. if module `foo.bar' was imported,
% then we insert the partially qualified symbol baz.quux/1;
% - if module `foo.bar' occurs in the "imported" section,
% i.e. if module `foo' was imported,
% then we insert the partially qualified symbol bar.baz.quux/1;
% - we always insert the fully qualified symbol foo.bar.baz.quux/1.
%
% The predicate `get_partial_qualifiers' returns all of the
% partial qualifiers for which we need to insert definitions,
% i.e. all the ones which are visible. For example,
% given as input `foo.bar.baz', it returns a list containing
% (1) `baz', iff `foo.bar' is imported, and
% (2) `bar.baz', iff `foo' is imported.
% Note that the caller will still need to handle the fully-qualified
% and fully-unqualified versions separately.
%
:- pred get_partial_qualifiers(module_name::in, partial_qualifier_info::in,
list(module_name)::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module libs.options.
:- import_module parse_tree.module_imports.
:- import_module parse_tree.prog_io_sym_name.
:- import_module parse_tree.prog_out.
:- import_module parse_tree.prog_util.
:- import_module assoc_list.
:- import_module map.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module solutions.
:- import_module term.
%-----------------------------------------------------------------------------%
module_qualify_items(Items0, Items, EventSpecMap0, EventSpecMap, Globals,
ModuleName, MaybeFileName, EventSpecFileName, !:Info, UndefTypes,
UndefModes, !Specs) :-
(
MaybeFileName = yes(_),
ReportErrors = yes
;
MaybeFileName = no,
ReportErrors = no
),
init_mq_info(Items0, Globals, ReportErrors, ModuleName, !:Info),
collect_mq_info(Items0, !Info),
do_module_qualify_items(Items0, Items, !Info, !Specs),
map.to_assoc_list(EventSpecMap0, EventSpecList0),
do_module_qualify_event_specs(EventSpecFileName,
EventSpecList0, EventSpecList, !Info, !Specs),
map.from_assoc_list(EventSpecList, EventSpecMap),
mq_info_get_type_error_flag(!.Info, UndefTypes),
mq_info_get_mode_error_flag(!.Info, UndefModes),
(
% Warn about any unused module imports in the interface.
% There is a special case involving type class instances that
% we need to handle here. Consider:
%
% :- module foo.
% :- interface.
%
% :- import_module bar.
% :- typeclass tc1(T) <= tc2(T).
% :- instance tc1(unit).
%
% where module bar exports the instance tc2(unit). We must import
% the module bar in the interface of the module foo in order for
% the superclass constraint on the instance tc1(unit) to be satisfied.
% However, at this stage of compilation we do not know that the
% instance tc2(unit) needs to be visible. (Knowing this would require
% a more extensive analysis of type classes and instances to be done
% in this module.)
%
% In order to prevent the import of the module bar being erroneously
% reported as unused we make the conservative assumption that any
% imported module that exports a type class instance is used in
% the interface of the importing module, except if the importing
% module itself exports _no_ type class instances.
%
MaybeFileName = yes(FileName),
mq_info_get_unused_interface_modules(!.Info, UnusedImports0),
mq_info_get_exported_instances_flag(!.Info, ModuleExportsInstances),
(
ModuleExportsInstances = yes,
mq_info_get_imported_instance_modules(!.Info, InstanceImports),
set.difference(UnusedImports0, InstanceImports, UnusedImports1)
;
ModuleExportsInstances = no,
UnusedImports1 = UnusedImports0
),
set.to_sorted_list(UnusedImports1, UnusedImports),
maybe_warn_unused_interface_imports(ModuleName, FileName,
UnusedImports, !Specs)
;
MaybeFileName = no
).
qualify_lambda_mode_list(Modes0, Modes, Context, !Info, !Specs) :-
mq_info_set_error_context(mqec_lambda_expr - Context, !Info),
qualify_mode_list(Modes0, Modes, !Info, !Specs).
qualify_clause_mode_list(Modes0, Modes, Context, !Info, !Specs) :-
mq_info_set_error_context(mqec_clause_mode_annotation - Context, !Info),
qualify_mode_list(Modes0, Modes, !Info, !Specs).
qualify_type_qualification(Type0, Type, Context, !Info, !Specs) :-
mq_info_set_error_context(mqec_type_qual - Context, !Info),
qualify_type(Type0, Type, !Info, !Specs).
:- type mq_info
---> mq_info(
% Modules which have been imported or used, i.e. the ones
% for which there was a `:- import_module' or `:- use_module'
% declaration in this module.
imported_modules :: set(module_name),
% Modules which have been imported or used in the interface.
interface_visible_modules :: set(module_name),
% Sets of all modules, types, insts, modes, and typeclasses
% visible in this module. Impl_types is the set of all types
% visible from the implementation of the module.
modules :: module_id_set,
types :: type_id_set,
impl_types :: type_id_set,
insts :: inst_id_set,
modes :: mode_id_set,
classes :: class_id_set,
% Modules imported in the interface that are not definitely
% needed in the interface.
unused_interface_modules :: set(module_name),
% Modules from which `:- instance' declarations have
% been imported.
imported_instance_modules :: set(module_name),
% Does this module export any type class instances?
exported_instances_flag :: bool,
% Import status of the current item.
import_status :: mq_import_status,
% The number of errors found.
num_errors :: int,
% Are there any undefined types or typeclasses.
type_error_flag :: bool,
% Are there any undefined insts or modes.
mode_error_flag :: bool,
% Do we want to report errors.
report_error_flag :: bool,
% The context of the current item.
error_context :: error_context,
% The name of the current module.
this_module :: module_name,
% Must uses of the current item be explicitly module qualified.
need_qual_flag :: need_qualifier,
maybe_recompilation_info :: maybe(recompilation_info)
).
:- type partial_qualifier_info
---> partial_qualifier_info(module_id_set).
mq_info_get_partial_qualifier_info(MQInfo, QualifierInfo) :-
mq_info_get_modules(MQInfo, ModuleIdSet),
QualifierInfo = partial_qualifier_info(ModuleIdSet).
% We only need to keep track of what is exported and what isn't,
% so we use a simpler data type here than hlds_pred.import_status.
%
:- type mq_import_status
---> mq_status_exported
; mq_status_local
; mq_status_imported(import_locn)
; mq_status_abstract_imported.
% Pass over the item list collecting all defined module, type, mode and
% inst ids, all module synonym definitions, and the names of all
% modules imported in the interface.
%
:- pred collect_mq_info(list(item)::in, mq_info::in, mq_info::out) is det.
collect_mq_info([], !Info).
collect_mq_info([Item | Items], !Info) :-
(
Item = item_module_defn(ItemModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _SeqNum),
ModuleDefn = md_transitively_imported
->
% Don't process the transitively imported items (from `.int2' files).
% They can't be used in the current module.
true
;
collect_mq_info_item(Item, !Info),
collect_mq_info(Items, !Info)
).
:- pred collect_mq_info_item(item::in, mq_info::in, mq_info::out) is det.
collect_mq_info_item(Item, !Info) :-
(
Item = item_module_start(ItemModuleStart),
ItemModuleStart = item_module_start_info(ModuleName, _, _),
add_module_defn(ModuleName, !Info)
;
Item = item_module_end(_)
;
Item = item_module_defn(ItemModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
process_module_defn(ModuleDefn, !Info)
;
Item = item_type_defn(ItemTypeDefn),
ItemTypeDefn = item_type_defn_info(_, SymName, Params, _, _, _, _),
( mq_info_get_import_status(!.Info, mq_status_abstract_imported) ->
% This item is not visible in the current module.
true
;
list.length(Params, Arity),
mq_info_get_types(!.Info, Types0),
mq_info_get_impl_types(!.Info, ImplTypes0),
mq_info_get_need_qual_flag(!.Info, NeedQualifier),
id_set_insert(NeedQualifier, mq_id(SymName, Arity), Types0, Types),
id_set_insert(NeedQualifier, mq_id(SymName, Arity),
ImplTypes0, ImplTypes),
mq_info_set_types(Types, !Info),
mq_info_set_impl_types(ImplTypes, !Info)
)
;
Item = item_inst_defn(ItemInstDefn),
ItemInstDefn = item_inst_defn_info(_, SymName, Params, _, _, _, _),
( mq_info_get_import_status(!.Info, mq_status_abstract_imported) ->
% This item is not visible in the current module.
true
;
list.length(Params, Arity),
mq_info_get_insts(!.Info, Insts0),
mq_info_get_need_qual_flag(!.Info, NeedQualifier),
id_set_insert(NeedQualifier, mq_id(SymName, Arity), Insts0, Insts),
mq_info_set_insts(Insts, !Info)
)
;
Item = item_mode_defn(ItemModeDefn),
ItemModeDefn = item_mode_defn_info(_, SymName, Params, _, _, _, _),
( mq_info_get_import_status(!.Info, mq_status_abstract_imported) ->
% This item is not visible in the current module.
true
;
list.length(Params, Arity),
mq_info_get_modes(!.Info, Modes0),
mq_info_get_need_qual_flag(!.Info, NeedQualifier),
id_set_insert(NeedQualifier, mq_id(SymName, Arity), Modes0, Modes),
mq_info_set_modes(Modes, !Info)
)
;
Item = item_promise(ItemPromise),
ItemPromise = item_promise_info(_PromiseType, Goal, _ProgVarSet,
_UnivVars, _Context, _SeqNum),
process_assert(Goal, SymNames, Success),
(
Success = yes,
list.foldl(collect_mq_info_qualified_symname, SymNames, !Info)
;
% Any unqualified symbol in the promise might come from *any* of
% the imported modules. There's no way for us to tell which ones.
% So we conservatively assume that it uses all of them.
Success = no,
set.init(UnusedInterfaceModules),
mq_info_set_unused_interface_modules(UnusedInterfaceModules, !Info)
)
;
Item = item_typeclass(ItemTypeClass),
ItemTypeClass = item_typeclass_info(_, _, SymName, Params, _, _, _, _),
( mq_info_get_import_status(!.Info, mq_status_abstract_imported) ->
% This item is not visible in the current module.
true
;
list.length(Params, Arity),
mq_info_get_classes(!.Info, Classes0),
mq_info_get_need_qual_flag(!.Info, NeedQualifier),
id_set_insert(NeedQualifier, mq_id(SymName, Arity),
Classes0, Classes),
mq_info_set_classes(Classes, !Info)
)
;
Item = item_instance(ItemInstance),
( mq_info_get_import_status(!.Info, mq_status_imported(_)) ->
InstanceModule = ItemInstance ^ ci_module_containing_instance,
mq_info_get_imported_instance_modules(!.Info,
ImportedInstanceModules0),
set.insert(InstanceModule,
ImportedInstanceModules0, ImportedInstanceModules),
mq_info_set_imported_instance_modules(ImportedInstanceModules,
!Info)
;
true
)
;
( Item = item_clause(_)
; Item = item_pred_decl(_)
; Item = item_mode_decl(_)
; Item = item_pragma(_)
; Item = item_initialise(_)
; Item = item_finalise(_)
; Item = item_mutable(_)
; Item = item_nothing(_)
)
% Do nothing.
).
:- pred collect_mq_info_qualified_symname(sym_name::in,
mq_info::in, mq_info::out) is det.
collect_mq_info_qualified_symname(SymName, !Info) :-
(
SymName = qualified(ModuleName, _),
mq_info_set_module_used(ModuleName, !Info)
;
SymName = unqualified(_),
unexpected($module, $pred, "unqualified")
).
% process_module_defn:
%
% - Update the import status.
%
% - For sub-module definitions (whether nested or separate,
% i.e. either `:- module foo.' or `:- include_module foo.'),
% add the module id to the module_id_set.
%
% - For import declarations (`:- import_module' or `:- use_module'),
% if we're currently in the interface section, then add the
% imported modules to the unused_interface_modules list.
%
:- pred process_module_defn(module_defn::in, mq_info::in, mq_info::out) is det.
process_module_defn(md_include_module(ModuleNameList), !Info) :-
list.foldl(add_module_defn, ModuleNameList, !Info).
process_module_defn(md_interface, !Info) :-
mq_info_set_import_status(mq_status_exported, !Info).
process_module_defn(md_implementation_but_exported_to_submodules, !Info) :-
mq_info_set_import_status(mq_status_local, !Info).
process_module_defn(md_implementation, !Info) :-
mq_info_set_import_status(mq_status_local, !Info).
process_module_defn(md_imported(Locn), !Info) :-
mq_info_set_import_status(mq_status_imported(Locn), !Info),
mq_info_set_need_qual_flag(may_be_unqualified, !Info).
process_module_defn(md_used(Locn), !Info) :-
mq_info_set_import_status(mq_status_imported(Locn), !Info),
mq_info_set_need_qual_flag(must_be_qualified, !Info).
process_module_defn(md_opt_imported, !Info) :-
mq_info_set_import_status(mq_status_imported(import_locn_implementation),
!Info),
mq_info_set_need_qual_flag(must_be_qualified, !Info).
process_module_defn(md_abstract_imported, !Info) :-
mq_info_set_import_status(mq_status_abstract_imported, !Info),
mq_info_set_need_qual_flag(must_be_qualified, !Info).
process_module_defn(md_transitively_imported, !Info) :-
unexpected($module, $pred, "transitively_imported item").
process_module_defn(md_external(_, _), !Info).
process_module_defn(md_export(_), !Info).
process_module_defn(md_import(Imports), !Info) :-
add_imports(Imports, !Info).
process_module_defn(md_use(Imports), !Info) :-
add_imports(Imports, !Info).
process_module_defn(md_version_numbers(_, _), !Info).
:- pred add_module_defn(module_name::in, mq_info::in, mq_info::out) is det.
add_module_defn(ModuleName, !Info) :-
mq_info_get_modules(!.Info, Modules0),
mq_info_get_need_qual_flag(!.Info, NeedQualifier),
Arity = 0,
id_set_insert(NeedQualifier, mq_id(ModuleName, Arity), Modules0, Modules),
mq_info_set_modules(Modules, !Info).
:- pred add_imports(list(module_specifier)::in, mq_info::in, mq_info::out)
is det.
add_imports(Imports, !Info) :-
mq_info_get_import_status(!.Info, Status),
% Modules imported from the the proper private interface of ancestors of
% the current module are treated as if they were directly imported
% by the current module.
(
( Status = mq_status_local
; Status = mq_status_exported
; Status = mq_status_imported(
import_locn_ancestor_private_interface_proper)
)
->
mq_info_get_imported_modules(!.Info, Modules0),
set.insert_list(Imports, Modules0, Modules),
mq_info_set_imported_modules(Modules, !Info)
;
true
),
% We check that all modules imported in the interface are
% used in the interface.
(
Status = mq_status_exported
->
mq_info_get_unused_interface_modules(!.Info, UnusedIntModules0),
set.insert_list(Imports, UnusedIntModules0, UnusedIntModules),
mq_info_set_unused_interface_modules(UnusedIntModules, !Info)
;
true
),
% Only modules imported in the interface or in the private
% interface of ancestor modules may be used in the interface.
(
( Status = mq_status_exported
; Status = mq_status_imported(
import_locn_ancestor_private_interface_proper)
)
->
mq_info_get_interface_visible_modules(!.Info, IntModules0),
set.insert_list(Imports, IntModules0, IntModules),
mq_info_set_interface_visible_modules(IntModules, !Info)
;
true
).
%-----------------------------------------------------------------------------%
% process_assert(G, SNs, B)
%
% Scan the goal, G, building the list of qualified symbols, SNs.
% If there exists a single unqualified symbol in G, the bool, B,
% will be set to no.
%
:- pred process_assert(goal::in, list(sym_name)::out, bool::out) is det.
% AAA Some more stuff to do accumulator introduction on, it
% would be better to rewrite using maybes and then to declare
% the maybe_and predicate to be associative.
% NB. accumulator introduction doesn't work on this case yet.
%
process_assert(Goal, Symbols, Success) :-
Goal = GoalExpr - _Context,
(
( GoalExpr = conj_expr(GA, GB)
; GoalExpr = par_conj_expr(GA, GB)
; GoalExpr = disj_expr(GA, GB)
; GoalExpr = implies_expr(GA, GB)
; GoalExpr = equivalent_expr(GA, GB)
),
process_assert(GA, SymbolsA, SuccessA),
process_assert(GB, SymbolsB, SuccessB),
Symbols = SymbolsA ++ SymbolsB,
bool.and(SuccessA, SuccessB, Success)
;
( GoalExpr = true_expr
; GoalExpr = fail_expr
),
Symbols = [],
Success = yes
;
( GoalExpr = not_expr(G)
; GoalExpr = some_expr(_, G)
; GoalExpr = some_state_vars_expr(_, G)
; GoalExpr = all_expr(_, G)
; GoalExpr = all_state_vars_expr(_, G)
; GoalExpr = promise_purity_expr(_, G)
; GoalExpr = promise_equivalent_solutions_expr(_, _, _, _, G)
; GoalExpr = promise_equivalent_solution_sets_expr(_, _, _, _, G)
; GoalExpr = promise_equivalent_solution_arbitrary_expr(_, _, _, _, G)
; GoalExpr = require_detism_expr(_, G)
; GoalExpr = require_complete_switch_expr(_, G)
; GoalExpr = trace_expr(_, _, _, _, G)
),
process_assert(G, Symbols, Success)
;
GoalExpr = try_expr(_, SubGoal, Then, MaybeElse, Catches,
MaybeCatchAny),
process_assert(SubGoal, SymbolsGoal, SuccessGoal),
process_assert(Then, SymbolsThen, SuccessThen),
maybe_process_assert(MaybeElse, SymbolsElse, SuccessElse),
list.map2(process_assert_catch, Catches,
SymbolsCatches, SuccessCatches),
(
MaybeCatchAny = yes(catch_any_expr(_, CatchAnyGoal)),
process_assert(CatchAnyGoal, SymbolsCatchAny, SuccessCatchAny)
;
MaybeCatchAny = no,
SymbolsCatchAny = [],
SuccessCatchAny = no
),
SymbolsLists = [SymbolsGoal, SymbolsThen, SymbolsElse, SymbolsCatchAny
| SymbolsCatches],
list.condense(SymbolsLists, Symbols),
SuccessLists = [SuccessGoal, SuccessThen, SuccessElse, SuccessCatchAny
| SuccessCatches],
bool.and_list(SuccessLists, Success)
;
GoalExpr = atomic_expr(_, _, _, MainGoal, OrElseGoals),
process_assert(MainGoal, SymbolsMainGoal, SuccessMainGoal),
process_assert_list(OrElseGoals,
SymbolsOrElseGoals, SuccessOrElseGoals),
Symbols = SymbolsMainGoal ++ SymbolsOrElseGoals,
bool.and(SuccessMainGoal, SuccessOrElseGoals, Success)
;
GoalExpr = if_then_else_expr(_, _, GA, GB, GC),
process_assert(GA, SymbolsA, SuccessA),
process_assert(GB, SymbolsB, SuccessB),
process_assert(GC, SymbolsC, SuccessC),
Symbols = SymbolsA ++ SymbolsB ++ SymbolsC,
bool.and(SuccessA, SuccessB, Success0),
bool.and(Success0, SuccessC, Success)
;
GoalExpr = event_expr(_Name, Args0),
list.map(term.coerce, Args0, Args),
( term_qualified_symbols_list(Args, SymbolsPrime) ->
Symbols = SymbolsPrime,
Success = yes
;
Symbols = [],
Success = no
)
;
GoalExpr = call_expr(SymName, Args0, _Purity),
(
SymName = qualified(_, _),
list.map(term.coerce, Args0, Args),
( term_qualified_symbols_list(Args, Symbols0) ->
Symbols = [SymName | Symbols0],
Success = yes
;
Symbols = [],
Success = no
)
;
SymName = unqualified(_),
Symbols = [],
Success = no
)
;
GoalExpr = unify_expr(LHS0, RHS0, _Purity),
term.coerce(LHS0, LHS),
term.coerce(RHS0, RHS),
(
term_qualified_symbols(LHS, SymbolsL),
term_qualified_symbols(RHS, SymbolsR)
->
list.append(SymbolsL, SymbolsR, Symbols),
Success = yes
;
Symbols = [],
Success = no
)
).
:- pred maybe_process_assert(maybe(goal)::in, list(sym_name)::out, bool::out)
is det.
maybe_process_assert(no, [], yes).
maybe_process_assert(yes(Goal), Symbols, Success) :-
process_assert(Goal, Symbols, Success).
:- pred process_assert_catch(catch_expr::in, list(sym_name)::out, bool::out)
is det.
process_assert_catch(catch_expr(Pattern0, Goal), Symbols, Success) :-
term.coerce(Pattern0, Pattern),
(
term_qualified_symbols(Pattern, SymbolsPattern),
process_assert(Goal, SymbolsGoal, yes)
->
list.append(SymbolsPattern, SymbolsGoal, Symbols),
Success = yes
;
Symbols = [],
Success = no
).
% process_assert(G, SNs, B)
%
% Performs process_assert on a list of goals.
%
:- pred process_assert_list(list(goal)::in, list(sym_name)::out,
bool::out) is det.
process_assert_list(ExprList, Symbols, Success) :-
(
ExprList = [],
Symbols = [],
Success = yes
;
ExprList = [Expr | Rest],
process_assert(Expr, SymbolsE, SuccessE),
process_assert_list(Rest, SymbolsR, SuccessR),
list.append(SymbolsE, SymbolsR, Symbols),
bool.and(SuccessE, SuccessR, Success)
).
% term_qualified_symbols(T, S)
%
% Given a term, T, return the list of all the sym_names, S, in the term.
% The predicate fails if any sub-term of T is unqualified.
%
:- pred term_qualified_symbols(term::in, list(sym_name)::out) is semidet.
term_qualified_symbols(Term, Symbols) :-
( try_parse_sym_name_and_args(Term, SymName, Args) ->
SymName = qualified(_, _),
term_qualified_symbols_list(Args, Symbols0),
Symbols = [SymName | Symbols0]
;
Symbols = []
).
:- pred term_qualified_symbols_list(list(term)::in, list(sym_name)::out)
is semidet.
% Yeah one more place where accumulators will be introduced!
term_qualified_symbols_list([], []).
term_qualified_symbols_list([Term | Terms], Symbols) :-
term_qualified_symbols(Term, TermSymbols),
term_qualified_symbols_list(Terms, Symbols0),
list.append(Symbols0, TermSymbols, Symbols).
%-----------------------------------------------------------------------------%
% Iterate over the item list module qualifying all declarations.
% Stop when the :- imported or :- opt_imported pseudo-declaration
% is reached, since imported declarations should already be
% module qualified.
%
:- pred do_module_qualify_items(list(item)::in, list(item)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
do_module_qualify_items([], [], !Info, !Specs).
do_module_qualify_items([Item0 | Items0], [Item | Items], !Info, !Specs) :-
module_qualify_item(Item0, Item, Continue, !Info, !Specs),
(
Continue = yes,
do_module_qualify_items(Items0, Items, !Info, !Specs)
;
Continue = no,
Items = Items0
).
% Call predicates to qualify a single item.
%
:- pred module_qualify_item(item::in, item::out, bool::out,
mq_info::in, mq_info::out, list(error_spec)::in, list(error_spec)::out)
is det.
module_qualify_item(Item0, Item, Continue, !Info, !Specs) :-
(
( Item0 = item_module_start(_)
; Item0 = item_module_end(_)
; Item0 = item_clause(_)
; Item0 = item_initialise(_)
; Item0 = item_finalise(_)
; Item0 = item_promise(_)
; Item0 = item_nothing(_)
),
Item = Item0,
Continue = yes
;
Item0 = item_module_defn(ItemModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
update_import_status(ModuleDefn, !Info, Continue),
Item = Item0
;
Item0 = item_type_defn(ItemTypeDefn0),
ItemTypeDefn0 = item_type_defn_info(TVarSet, SymName, Params,
TypeDefn0, C, Context, SeqNum),
list.length(Params, Arity),
mq_info_set_error_context(mqec_type(mq_id(SymName, Arity)) - Context,
!Info),
qualify_type_defn(TypeDefn0, TypeDefn, !Info, !Specs),
ItemTypeDefn = item_type_defn_info(TVarSet, SymName, Params,
TypeDefn, C, Context, SeqNum),
Item = item_type_defn(ItemTypeDefn),
Continue = yes
;
Item0 = item_inst_defn(ItemInstDefn0),
ItemInstDefn0 = item_inst_defn_info(A, SymName, Params, InstDefn0, C,
Context, SeqNum),
list.length(Params, Arity),
mq_info_set_error_context(mqec_inst(mq_id(SymName, Arity)) - Context,
!Info),
qualify_inst_defn(InstDefn0, InstDefn, !Info, !Specs),
ItemInstDefn = item_inst_defn_info(A, SymName, Params, InstDefn, C,
Context, SeqNum),
Item = item_inst_defn(ItemInstDefn),
Continue = yes
;
Item0 = item_mode_defn(ItemModeDefn0),
ItemModeDefn0 = item_mode_defn_info(A, SymName, Params, ModeDefn0, C,
Context, SeqNum),
list.length(Params, Arity),
mq_info_set_error_context(mqec_mode(mq_id(SymName, Arity)) - Context,
!Info),
qualify_mode_defn(ModeDefn0, ModeDefn, !Info, !Specs),
ItemModeDefn = item_mode_defn_info(A, SymName, Params, ModeDefn, C,
Context, SeqNum),
Item = item_mode_defn(ItemModeDefn),
Continue = yes
;
Item0 = item_pred_decl(ItemPredDecl0),
ItemPredDecl0 = item_pred_decl_info(Origin, A, IVs, B, PredOrFunc,
SymName, TypesAndModes0, WithType0, WithInst0, C, D, E,
Constraints0, Context, SeqNum),
list.length(TypesAndModes0, Arity),
mq_info_set_error_context(
mqec_pred_or_func(PredOrFunc, mq_id(SymName, Arity)) - Context,
!Info),
qualify_types_and_modes(TypesAndModes0, TypesAndModes, !Info, !Specs),
qualify_prog_constraints(Constraints0, Constraints, !Info, !Specs),
map_fold2_maybe(qualify_type, WithType0, WithType, !Info, !Specs),
map_fold2_maybe(qualify_inst, WithInst0, WithInst, !Info, !Specs),
ItemPredDecl = item_pred_decl_info(Origin, A, IVs, B, PredOrFunc,
SymName, TypesAndModes, WithType, WithInst, C, D, E,
Constraints, Context, SeqNum),
Item = item_pred_decl(ItemPredDecl),
Continue = yes
;
Item0 = item_mode_decl(ItemModeDecl0),
ItemModeDecl0 = item_mode_decl_info(A, PredOrFunc, SymName, Modes0,
WithInst0, C, D, Context, SeqNum),
list.length(Modes0, Arity),
mq_info_set_error_context(
mqec_pred_or_func_mode(PredOrFunc, mq_id(SymName, Arity))
- Context,
!Info),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
map_fold2_maybe(qualify_inst, WithInst0, WithInst, !Info, !Specs),
ItemModeDecl = item_mode_decl_info(A, PredOrFunc, SymName, Modes,
WithInst, C, D, Context, SeqNum),
Item = item_mode_decl(ItemModeDecl),
Continue = yes
;
Item0 = item_pragma(ItemPragma0),
ItemPragma0 = item_pragma_info(Origin, Pragma0, Context, SeqNum),
mq_info_set_error_context(mqec_pragma(Pragma0) - Context, !Info),
qualify_pragma(Pragma0, Pragma, !Info, !Specs),
ItemPragma = item_pragma_info(Origin, Pragma, Context, SeqNum),
Item = item_pragma(ItemPragma),
Continue = yes
;
Item0 = item_typeclass(ItemTypeClass0),
ItemTypeClass0 = item_typeclass_info(Constraints0, FunDeps,
Name, Vars, Interface0, VarSet, Context, SeqNum),
list.length(Vars, Arity),
mq_info_set_error_context(mqec_class(mq_id(Name, Arity)) - Context,
!Info),
qualify_prog_constraint_list(Constraints0, Constraints, !Info, !Specs),
(
Interface0 = class_interface_abstract,
Interface = class_interface_abstract
;
Interface0 = class_interface_concrete(Methods0),
qualify_class_interface(Methods0, Methods, !Info, !Specs),
Interface = class_interface_concrete(Methods)
),
ItemTypeClass = item_typeclass_info(Constraints, FunDeps,
Name, Vars, Interface, VarSet, Context, SeqNum),
Item = item_typeclass(ItemTypeClass),
Continue = yes
;
Item0 = item_instance(ItemInstance0),
ItemInstance0 = item_instance_info(Constraints0, Name0,
Types0, OriginalTypes0, Body0, VarSet, ModName, Context, SeqNum),
list.length(Types0, Arity),
Id = mq_id(Name0, Arity),
mq_info_set_error_context(mqec_instance(Id) - Context, !Info),
( mq_info_get_import_status(!.Info, mq_status_exported) ->
mq_info_set_exported_instances_flag(yes, !Info)
;
true
),
% We don't qualify the implementation yet, since that requires
% us to resolve overloading.
qualify_prog_constraint_list(Constraints0, Constraints, !Info, !Specs),
qualify_class_name(Id, mq_id(Name, _), !Info, !Specs),
% XXX We don't want to keep the errors from the expansion of both
% forms of the instance types, since printing two error messages about
% one instance definition that make apparently contradictory
% assumptions about whether the instance types are equiv-type-expanded
% or not wouldd be confusing. However, I (zs) cannot think of any
% compelling reason right now for preferring the error messages
% from one version of the types over the other.
qualify_type_list(Types0, Types, !Info, !Specs),
qualify_type_list(OriginalTypes0, OriginalTypes, !Info, !.Specs, _),
qualify_instance_body(Name, Body0, Body),
ItemInstance = item_instance_info(Constraints, Name,
Types, OriginalTypes, Body, VarSet, ModName, Context, SeqNum),
Item = item_instance(ItemInstance),
Continue = yes
;
Item0 = item_mutable(ItemMutable0),
do_module_qualify_mutable(ItemMutable0, ItemMutable, !Info, !Specs),
Item = item_mutable(ItemMutable),
Continue = yes
).
:- pred do_module_qualify_mutable(item_mutable_info::in, item_mutable_info::out,
mq_info::in, mq_info::out, list(error_spec)::in, list(error_spec)::out)
is det.
do_module_qualify_mutable(ItemMutable0, ItemMutable, !Info, !Specs) :-
ItemMutable0 = item_mutable_info(Name, Type0, InitTerm, Inst0,
Attrs, Varset, Context, SeqNum),
mq_info_set_error_context(mqec_mutable(Name) - Context, !Info),
qualify_type(Type0, Type, !Info, !Specs),
qualify_inst(Inst0, Inst, !Info, !Specs),
ItemMutable = item_mutable_info(Name, Type, InitTerm, Inst,
Attrs, Varset, Context, SeqNum).
:- pred do_module_qualify_event_specs(string::in,
assoc_list(string, event_spec)::in, assoc_list(string, event_spec)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
do_module_qualify_event_specs(_, [], [], !Info, !Specs).
do_module_qualify_event_specs(FileName,
[Name - Spec0 | NameSpecs0], [Name - Spec | NameSpecs],
!Info, !Specs) :-
do_module_qualify_event_spec(FileName, Spec0, Spec, !Info, !Specs),
do_module_qualify_event_specs(FileName, NameSpecs0, NameSpecs,
!Info, !Specs).
:- pred do_module_qualify_event_spec(string::in,
event_spec::in, event_spec::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
do_module_qualify_event_spec(FileName, EventSpec0, EventSpec, !Info, !Specs) :-
EventSpec0 = event_spec(EventNumber, EventName, EventLineNumber,
Attrs0, SynthAttrNumOrder),
list.map_foldl2(
do_module_qualify_event_attr(EventName, FileName, EventLineNumber),
Attrs0, Attrs, !Info, !Specs),
EventSpec = event_spec(EventNumber, EventName, EventLineNumber,
Attrs, SynthAttrNumOrder).
:- pred do_module_qualify_event_attr(string::in, string::in, int::in,
event_attribute::in, event_attribute::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
do_module_qualify_event_attr(EventName, FileName, LineNumber, Attr0, Attr,
!Info, !Specs) :-
Attr0 = event_attribute(AttrNum, AttrName, AttrType0, AttrMode0,
MaybeSynthCall),
MQErrorContext = mqec_event_spec_attr(EventName, AttrName),
Context = context(FileName, LineNumber),
mq_info_set_error_context(MQErrorContext - Context, !Info),
qualify_type(AttrType0, AttrType, !Info, !Specs),
qualify_mode(AttrMode0, AttrMode, !Info, !Specs),
Attr = event_attribute(AttrNum, AttrName, AttrType, AttrMode,
MaybeSynthCall).
:- pred update_import_status(module_defn::in, mq_info::in, mq_info::out,
bool::out) is det.
update_import_status(md_opt_imported, !Info, no).
update_import_status(md_abstract_imported, !Info, yes) :-
mq_info_set_import_status(mq_status_abstract_imported, !Info).
update_import_status(md_transitively_imported, !Info, no).
update_import_status(md_interface, !Info, yes) :-
mq_info_set_import_status(mq_status_exported, !Info).
update_import_status(md_implementation, !Info, yes) :-
mq_info_set_import_status(mq_status_local, !Info).
update_import_status(md_implementation_but_exported_to_submodules, !Info, yes)
:-
mq_info_set_import_status(mq_status_local, !Info).
update_import_status(md_imported(_), !Info, no).
update_import_status(md_used(_), !Info, no).
update_import_status(md_external(_, _), !Info, yes).
update_import_status(md_export(_), !Info, yes).
update_import_status(md_import(_), !Info, yes).
update_import_status(md_use(_), !Info, yes).
update_import_status(md_version_numbers(_, _), !Info, yes).
update_import_status(md_include_module(_), !Info, yes) :-
% The sub-module might make use of *any* of the imported modules.
% There's no way for us to tell which ones.
% So we conservatively assume that it uses all of them.
set.init(UnusedInterfaceModules),
mq_info_set_unused_interface_modules(UnusedInterfaceModules, !Info).
% Qualify the constructors or other types in a type definition.
%
:- pred qualify_type_defn(type_defn::in, type_defn::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type_defn(
parse_tree_du_type(Ctors0, MaybeUserEqComp0, MaybeDirectArgCtors0),
parse_tree_du_type(Ctors, MaybeUserEqComp, MaybeDirectArgCtors),
!Info, !Specs) :-
qualify_constructors(Ctors0, Ctors, !Info, !Specs),
% User-defined equality pred names will be converted into predicate calls
% and then module-qualified after type analysis (during mode analysis).
% That way they get full type overloading resolution, etc. Thus we don't
% module-qualify them here.
MaybeUserEqComp = MaybeUserEqComp0,
MaybeDirectArgCtors = MaybeDirectArgCtors0.
qualify_type_defn(parse_tree_eqv_type(Type0), parse_tree_eqv_type(Type),
!Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs).
qualify_type_defn(parse_tree_abstract_type(_) @ Defn, Defn, !Info, !Specs).
qualify_type_defn(parse_tree_foreign_type(_, _, _) @ Defn, Defn, !Info,
!Specs).
qualify_type_defn(parse_tree_solver_type(SolverTypeDetails0, MaybeUserEqComp),
parse_tree_solver_type(SolverTypeDetails, MaybeUserEqComp),
!Info, !Specs) :-
SolverTypeDetails0 = solver_type_details(RepnType0, InitPred,
GroundInst0, AnyInst0, MutableItems0),
qualify_type(RepnType0, RepnType, !Info, !Specs),
qualify_inst(GroundInst0, GroundInst, !Info, !Specs),
qualify_inst(AnyInst0, AnyInst, !Info, !Specs),
qualify_constraint_stores(MutableItems0, MutableItems, !Info, !Specs),
SolverTypeDetails = solver_type_details(RepnType, InitPred,
GroundInst, AnyInst, MutableItems).
:- pred qualify_constraint_stores(
list(item_mutable_info)::in, list(item_mutable_info)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_constraint_stores([], [], !Info, !Specs).
qualify_constraint_stores([Mutable0 | Mutables0], [Mutable | Mutables],
!Info, !Specs) :-
do_module_qualify_mutable(Mutable0, Mutable, !Info, !Specs),
qualify_constraint_stores(Mutables0, Mutables, !Info, !Specs).
:- pred qualify_constructors(list(constructor)::in, list(constructor)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_constructors([], [], !Info, !Specs).
qualify_constructors([Ctor0 | Ctors0], [Ctor | Ctors], !Info, !Specs) :-
Ctor0 = ctor(ExistQVars, Constraints0, SymName, Args0, Ctxt),
qualify_constructor_arg_list(Args0, Args, !Info, !Specs),
qualify_constructors(Ctors0, Ctors, !Info, !Specs),
qualify_prog_constraint_list(Constraints0, Constraints, !Info, !Specs),
Ctor = ctor(ExistQVars, Constraints, SymName, Args, Ctxt).
% Qualify the inst parameters of an inst definition.
%
:- pred qualify_inst_defn(inst_defn::in, inst_defn::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_inst_defn(eqv_inst(Inst0), eqv_inst(Inst), !Info, !Specs) :-
qualify_inst(Inst0, Inst, !Info, !Specs).
qualify_inst_defn(abstract_inst, abstract_inst, !Info, !Specs).
% Qualify the mode parameter of an equivalence mode definition.
%
:- pred qualify_mode_defn(mode_defn::in, mode_defn::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_mode_defn(eqv_mode(Mode0), eqv_mode(Mode), !Info, !Specs) :-
qualify_mode(Mode0, Mode, !Info, !Specs).
% Qualify a list of items of the form Type::Mode, as in a
% predicate declaration.
%
:- pred qualify_types_and_modes(list(type_and_mode)::in,
list(type_and_mode)::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_types_and_modes([], [], !Info, !Specs).
qualify_types_and_modes([TypeAndMode0 | TypesAndModes0],
[TypeAndMode | TypesAndModes], !Info, !Specs) :-
qualify_type_and_mode(TypeAndMode0, TypeAndMode, !Info, !Specs),
qualify_types_and_modes(TypesAndModes0, TypesAndModes, !Info, !Specs).
:- pred qualify_type_and_mode(type_and_mode::in, type_and_mode::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type_and_mode(type_only(Type0), type_only(Type), !Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs).
qualify_type_and_mode(type_and_mode(Type0, Mode0), type_and_mode(Type, Mode),
!Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs),
qualify_mode(Mode0, Mode, !Info, !Specs).
:- pred qualify_mode_list(list(mer_mode)::in, list(mer_mode)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_mode_list([], [], !Info, !Specs).
qualify_mode_list([Mode0 | Modes0], [Mode | Modes], !Info, !Specs) :-
qualify_mode(Mode0, Mode, !Info, !Specs),
qualify_mode_list(Modes0, Modes, !Info, !Specs).
:- pred qualify_mode(mer_mode::in, mer_mode::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_mode((InstA0 -> InstB0), (InstA -> InstB), !Info, !Specs) :-
qualify_inst(InstA0, InstA, !Info, !Specs),
qualify_inst(InstB0, InstB, !Info, !Specs).
qualify_mode(user_defined_mode(SymName0, Insts0),
user_defined_mode(SymName, Insts), !Info, !Specs) :-
qualify_inst_list(Insts0, Insts, !Info, !Specs),
list.length(Insts, Arity),
mq_info_get_modes(!.Info, Modes),
find_unique_match(mq_id(SymName0, Arity), mq_id(SymName, _),
Modes, mode_id, !Info, !Specs).
:- pred qualify_inst_list(list(mer_inst)::in, list(mer_inst)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_inst_list([], [], !Info, !Specs).
qualify_inst_list([Inst0 | Insts0], [Inst | Insts], !Info, !Specs) :-
qualify_inst(Inst0, Inst, !Info, !Specs),
qualify_inst_list(Insts0, Insts, !Info, !Specs).
% Qualify a single inst.
%
:- pred qualify_inst(mer_inst::in, mer_inst::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_inst(Inst0, Inst, !Info, !Specs) :-
(
Inst0 = any(Uniq, HOInstInfo0),
qualify_ho_inst_info(HOInstInfo0, HOInstInfo, !Info, !Specs),
Inst = any(Uniq, HOInstInfo)
;
( Inst0 = free
; Inst0 = not_reached
; Inst0 = inst_var(_)
),
Inst = Inst0
;
Inst0 = free(_),
unexpected($module, $pred, "compiler generated inst not expected")
;
Inst0 = bound(Uniq, InstResults0, BoundInsts0),
(
( InstResults0 = inst_test_results_fgtc
; InstResults0 = inst_test_no_results
)
;
InstResults0 = inst_test_results(_, _, _, _),
unexpected($module, $pred, "compiler generated inst not expected")
),
qualify_bound_inst_list(BoundInsts0, BoundInsts, !Info, !Specs),
Inst = bound(Uniq, InstResults0, BoundInsts)
;
Inst0 = ground(Uniq, HOInstInfo0),
qualify_ho_inst_info(HOInstInfo0, HOInstInfo, !Info, !Specs),
Inst = ground(Uniq, HOInstInfo)
;
Inst0 = constrained_inst_vars(Vars, SubInst0),
qualify_inst(SubInst0, SubInst, !Info, !Specs),
Inst = constrained_inst_vars(Vars, SubInst)
;
Inst0 = defined_inst(InstName0),
qualify_inst_name(InstName0, InstName, !Info, !Specs),
Inst = defined_inst(InstName)
;
Inst0 = abstract_inst(Name, Args0),
qualify_inst_list(Args0, Args, !Info, !Specs),
Inst = abstract_inst(Name, Args)
).
:- pred qualify_ho_inst_info(ho_inst_info::in, ho_inst_info::out,
mq_info::in, mq_info::out, list(error_spec)::in, list(error_spec)::out)
is det.
qualify_ho_inst_info(HOInstInfo0, HOInstInfo, !Info, !Specs) :-
(
HOInstInfo0 = higher_order(pred_inst_info(A, Modes0, MaybeArgRegs,
Det)),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
HOInstInfo = higher_order(pred_inst_info(A, Modes, MaybeArgRegs, Det))
;
HOInstInfo0 = none,
HOInstInfo = none
).
% Find the unique inst_id that matches this inst, and qualify
% the argument insts.
%
:- pred qualify_inst_name(inst_name::in, inst_name::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_inst_name(user_inst(SymName0, Insts0), user_inst(SymName, Insts),
!Info, !Specs) :-
qualify_inst_list(Insts0, Insts, !Info, !Specs),
(
% Check for a variable inst constructor.
SymName0 = unqualified("")
->
mq_info_get_error_context(!.Info, ErrorContext),
report_invalid_user_inst(SymName0, Insts, ErrorContext, !Specs),
mq_info_set_error_flag(inst_id, !Info),
SymName = SymName0
;
list.length(Insts0, Arity),
mq_info_get_insts(!.Info, InstIds),
find_unique_match(mq_id(SymName0, Arity), mq_id(SymName, _),
InstIds, inst_id, !Info, !Specs)
).
qualify_inst_name(merge_inst(_, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(unify_inst(_, _, _, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(ground_inst(_, _, _, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(any_inst(_, _, _, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(shared_inst(_), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(mostly_uniq_inst(_), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(typed_ground(_, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
qualify_inst_name(typed_inst(_, _), _, !Info, !Specs) :-
unexpected($module, $pred, "compiler generated inst unexpected").
% Qualify an inst of the form bound(functor(...)).
%
:- pred qualify_bound_inst_list(list(bound_inst)::in, list(bound_inst)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_bound_inst_list([], [], !Info, !Specs).
qualify_bound_inst_list([bound_functor(ConsId, Insts0) | BoundInsts0],
[bound_functor(ConsId, Insts) | BoundInsts], !Info, !Specs) :-
(
ConsId = cons(Name, Arity, _),
Id = item_name(Name, Arity),
update_recompilation_info(
recompilation.record_used_item(functor_item, Id, Id), !Info)
;
( ConsId = tuple_cons(_)
; ConsId = closure_cons(_, _)
; ConsId = int_const(_)
; ConsId = float_const(_)
; ConsId = char_const(_)
; ConsId = string_const(_)
; ConsId = impl_defined_const(_)
; ConsId = type_ctor_info_const(_, _, _)
; ConsId = base_typeclass_info_const(_, _, _, _)
; ConsId = type_info_cell_constructor(_)
; ConsId = typeclass_info_cell_constructor
; ConsId = type_info_const(_)
; ConsId = typeclass_info_const(_)
; ConsId = ground_term_const(_, _)
; ConsId = tabling_info_const(_)
; ConsId = table_io_decl(_)
; ConsId = deep_profiling_proc_layout(_)
)
),
qualify_inst_list(Insts0, Insts, !Info, !Specs),
qualify_bound_inst_list(BoundInsts0, BoundInsts, !Info, !Specs).
:- pred qualify_constructor_arg_list(list(constructor_arg)::in,
list(constructor_arg)::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_constructor_arg_list([], [], !Info, !Specs).
qualify_constructor_arg_list([Arg0 | Args0], [Arg | Args], !Info, !Specs) :-
qualify_type(Arg0 ^ arg_type, Type, !Info, !Specs),
Arg = Arg0 ^ arg_type := Type,
qualify_constructor_arg_list(Args0, Args, !Info, !Specs).
:- pred qualify_type_list(list(mer_type)::in, list(mer_type)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type_list([], [], !Info, !Specs).
qualify_type_list([Type0 | Types0], [Type | Types], !Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs),
qualify_type_list(Types0, Types, !Info, !Specs).
:- pred qualify_maybe_type(maybe(mer_type)::in, maybe(mer_type)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_maybe_type(no, no, !Info, !Specs).
qualify_maybe_type(yes(Type0), yes(Type), !Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs).
% Qualify a type and its argument types.
%
:- pred qualify_type(mer_type::in, mer_type::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type(type_variable(Var, Kind), type_variable(Var, Kind), !Info,
!Specs).
qualify_type(defined_type(SymName0, Args0, Kind),
defined_type(SymName, Args, Kind), !Info, !Specs) :-
Arity = list.length(Args0),
TypeCtorId0 = mq_id(SymName0, Arity),
mq_info_get_types(!.Info, Types),
find_unique_match(TypeCtorId0, TypeCtorId, Types, type_id, !Info, !Specs),
TypeCtorId = mq_id(SymName, _),
qualify_type_list(Args0, Args, !Info, !Specs).
qualify_type(builtin_type(BuiltinType), builtin_type(BuiltinType), !Info,
!Specs) :-
% The types `int', `float', and `string' are builtin types, defined by
% the compiler, but arguably they ought to be defined in int.m, float.m,
% and string.m, and so if someone uses the type `int' in the interface,
% then we don't want to warn about `import_module int' in the interface.
% We don't do the same for `character', since the corresponding library
% module `char' will be flagged as used in the interface if the type
% `char' is used.
(
BuiltinType = builtin_type_int,
mq_info_set_module_used(unqualified("int"), !Info)
;
BuiltinType = builtin_type_float,
mq_info_set_module_used(unqualified("float"), !Info)
;
BuiltinType = builtin_type_string,
mq_info_set_module_used(unqualified("string"), !Info)
;
BuiltinType = builtin_type_char
).
qualify_type(higher_order_type(Args0, MaybeRet0, Purity, EvalMethod),
higher_order_type(Args, MaybeRet, Purity, EvalMethod),
!Info, !Specs) :-
qualify_type_list(Args0, Args, !Info, !Specs),
qualify_maybe_type(MaybeRet0, MaybeRet, !Info, !Specs).
qualify_type(tuple_type(Args0, Kind), tuple_type(Args, Kind), !Info, !Specs) :-
qualify_type_list(Args0, Args, !Info, !Specs).
qualify_type(apply_n_type(Var, Args0, Kind), apply_n_type(Var, Args, Kind),
!Info, !Specs) :-
qualify_type_list(Args0, Args, !Info, !Specs).
qualify_type(kinded_type(Type0, Kind), kinded_type(Type, Kind),
!Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs).
:- pred qualify_type_ctor(type_ctor::in, type_ctor::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type_ctor(!TypeCtor, !Info, !Specs) :-
!.TypeCtor = type_ctor(SymName0, Arity),
( is_builtin_atomic_type(!.TypeCtor) ->
SymName = SymName0
;
TypeCtorId0 = mq_id(SymName0, Arity),
mq_info_get_types(!.Info, Types),
find_unique_match(TypeCtorId0, TypeCtorId, Types, type_id,
!Info, !Specs),
TypeCtorId = mq_id(SymName, _)
),
!:TypeCtor = type_ctor(SymName, Arity).
:- pred qualify_pragma(pragma_type::in, pragma_type::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_pragma(Pragma0, Pragma, !Info, !Specs) :-
(
( Pragma0 = pragma_source_file(_)
; Pragma0 = pragma_foreign_decl(_)
; Pragma0 = pragma_foreign_code(_)
; Pragma0 = pragma_foreign_import_module(_)
; Pragma0 = pragma_inline(_)
; Pragma0 = pragma_no_inline(_)
; Pragma0 = pragma_obsolete(_)
; Pragma0 = pragma_no_detism_warning(_)
; Pragma0 = pragma_unused_args(_)
; Pragma0 = pragma_exceptions(_)
; Pragma0 = pragma_trailing_info(_)
; Pragma0 = pragma_mm_tabling_info(_)
; Pragma0 = pragma_fact_table(_)
; Pragma0 = pragma_promise_pure(_)
; Pragma0 = pragma_promise_semipure(_)
; Pragma0 = pragma_promise_eqv_clauses(_)
; Pragma0 = pragma_terminates(_)
; Pragma0 = pragma_does_not_terminate(_)
; Pragma0 = pragma_check_termination(_)
; Pragma0 = pragma_mode_check_clauses(_)
; Pragma0 = pragma_require_feature_set(_)
),
Pragma = Pragma0
;
Pragma0 = pragma_reserve_tag(_TypeCtor0),
% XXX We should be module qualifying TypeCtor0 here,
% not in add_pragma.m. However, the code in add_pragma.m
% does generate better error messages than qualify_type_ctor does;
% this implies we should fix qualify_type_ctor.
Pragma = Pragma0
;
Pragma0 = pragma_foreign_export_enum(FEEInfo0),
FEEInfo0 = pragma_info_foreign_export_enum(Lang, TypeCtor0,
Attributes, Overrides),
qualify_type_ctor(TypeCtor0, TypeCtor, !Info, !Specs),
FEEInfo = pragma_info_foreign_export_enum(Lang, TypeCtor,
Attributes, Overrides),
Pragma = pragma_foreign_export_enum(FEEInfo)
;
Pragma0 = pragma_foreign_enum(FEInfo0),
FEInfo0 = pragma_info_foreign_enum(Lang, TypeCtor0, Values),
qualify_type_ctor(TypeCtor0, TypeCtor, !Info, !Specs),
FEInfo = pragma_info_foreign_enum(Lang, TypeCtor, Values),
Pragma = pragma_foreign_enum(FEInfo)
;
Pragma0 = pragma_foreign_proc(FPInfo0),
FPInfo0 = pragma_info_foreign_proc(Attrs0, Name, PredOrFunc,
Vars0, Varset, InstVarset, Impl),
qualify_pragma_vars(Vars0, Vars, !Info, !Specs),
UserSharing0 = get_user_annotated_sharing(Attrs0),
qualify_user_sharing(UserSharing0, UserSharing, !Info, !Specs),
set_user_annotated_sharing(UserSharing, Attrs0, Attrs),
FPInfo = pragma_info_foreign_proc(Attrs, Name, PredOrFunc,
Vars, Varset, InstVarset, Impl),
Pragma = pragma_foreign_proc(FPInfo)
;
Pragma0 = pragma_oisu(OISUInfo0),
OISUInfo0 = pragma_info_oisu(TypeCtor0, CreatorPreds,
MutatorPreds, DestructorPreds),
% XXX Preds
qualify_type_ctor(TypeCtor0, TypeCtor, !Info, !Specs),
OISUInfo = pragma_info_oisu(TypeCtor, CreatorPreds,
MutatorPreds, DestructorPreds),
Pragma = pragma_oisu(OISUInfo)
;
Pragma0 = pragma_tabled(TabledInfo0),
TabledInfo0 = pragma_info_tabled(EvalMethod, PredNameArityPF,
MModes0, Attrs),
(
MModes0 = yes(Modes0),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
MModes = yes(Modes)
;
MModes0 = no,
MModes = no
),
TabledInfo = pragma_info_tabled(EvalMethod, PredNameArityPF,
MModes, Attrs),
Pragma = pragma_tabled(TabledInfo)
;
Pragma0 = pragma_foreign_proc_export(FPEInfo0),
FPEInfo0 = pragma_info_foreign_proc_export(Lang, PredNameModesPF0,
CFunc),
PredNameModesPF0 = pred_name_modes_pf(Name, Modes0, PredOrFunc),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
PredNameModesPF = pred_name_modes_pf(Name, Modes, PredOrFunc),
FPEInfo = pragma_info_foreign_proc_export(Lang, PredNameModesPF,
CFunc),
Pragma = pragma_foreign_proc_export(FPEInfo)
;
Pragma0 = pragma_type_spec(TypeSpecInfo0),
TypeSpecInfo0 = pragma_info_type_spec(A, B, C, D, MaybeModes0,
Subst0, G, H),
(
MaybeModes0 = yes(Modes0),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
MaybeModes = yes(Modes)
;
MaybeModes0 = no,
MaybeModes = no
),
qualify_type_spec_subst(Subst0, Subst, !Info, !Specs),
TypeSpecInfo = pragma_info_type_spec(A, B, C, D, MaybeModes,
Subst, G, H),
Pragma = pragma_type_spec(TypeSpecInfo)
;
Pragma0 = pragma_termination_info(TermInfo0),
TermInfo0 = pragma_info_termination_info(PredNameModesPF0, Args, Term),
PredNameModesPF0 = pred_name_modes_pf(SymName, ModeList0, PredOrFunc),
qualify_mode_list(ModeList0, ModeList, !Info, !Specs),
PredNameModesPF = pred_name_modes_pf(SymName, ModeList, PredOrFunc),
TermInfo = pragma_info_termination_info(PredNameModesPF, Args, Term),
Pragma = pragma_termination_info(TermInfo)
;
Pragma0 = pragma_structure_sharing(SharingInfo0),
SharingInfo0 = pragma_info_structure_sharing(PredNameModesPF0,
Vars, Types, Sharing),
PredNameModesPF0 = pred_name_modes_pf(SymName, ModeList0, PredOrFunc),
qualify_mode_list(ModeList0, ModeList, !Info, !Specs),
PredNameModesPF = pred_name_modes_pf(SymName, ModeList, PredOrFunc),
SharingInfo = pragma_info_structure_sharing(PredNameModesPF,
Vars, Types, Sharing),
Pragma = pragma_structure_sharing(SharingInfo)
;
Pragma0 = pragma_structure_reuse(ReuseInfo0),
ReuseInfo0 = pragma_info_structure_reuse(PredNameModesPF0,
Vars, Types, ReuseTuples),
PredNameModesPF0 = pred_name_modes_pf(SymName, ModeList0, PredOrFunc),
qualify_mode_list(ModeList0, ModeList, !Info, !Specs),
PredNameModesPF = pred_name_modes_pf(SymName, ModeList, PredOrFunc),
ReuseInfo = pragma_info_structure_reuse(PredNameModesPF,
Vars, Types, ReuseTuples),
Pragma = pragma_structure_reuse(ReuseInfo)
;
Pragma0 = pragma_termination2_info(Term2Info0),
Term2Info0 = pragma_info_termination2_info(PredNameModesPF0,
SuccessArgs, FailureArgs, Term),
PredNameModesPF0 = pred_name_modes_pf(SymName, ModeList0, PredOrFunc),
qualify_mode_list(ModeList0, ModeList, !Info, !Specs),
PredNameModesPF = pred_name_modes_pf(SymName, ModeList, PredOrFunc),
Term2Info = pragma_info_termination2_info(PredNameModesPF,
SuccessArgs, FailureArgs, Term),
Pragma = pragma_termination2_info(Term2Info)
).
:- pred qualify_pragma_vars(list(pragma_var)::in, list(pragma_var)::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_pragma_vars([], [], !Info, !Specs).
qualify_pragma_vars([PragmaVar0 | PragmaVars0], [PragmaVar | PragmaVars],
!Info, !Specs) :-
PragmaVar0 = pragma_var(Var, Name, Mode0, Box),
qualify_mode(Mode0, Mode, !Info, !Specs),
PragmaVar = pragma_var(Var, Name, Mode, Box),
qualify_pragma_vars(PragmaVars0, PragmaVars, !Info, !Specs).
:- pred qualify_type_spec_subst(assoc_list(tvar, mer_type)::in,
assoc_list(tvar, mer_type)::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_type_spec_subst([], [], !Info, !Specs).
qualify_type_spec_subst([Var - Type0 | Subst0], [Var - Type | Subst],
!Info, !Specs) :-
qualify_type(Type0, Type, !Info, !Specs),
qualify_type_spec_subst(Subst0, Subst, !Info, !Specs).
:- pred qualify_prog_constraints(prog_constraints::in,
prog_constraints::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_prog_constraints(constraints(UnivCs0, ExistCs0),
constraints(UnivCs, ExistCs), !Info, !Specs) :-
qualify_prog_constraint_list(UnivCs0, UnivCs, !Info, !Specs),
qualify_prog_constraint_list(ExistCs0, ExistCs, !Info, !Specs).
:- pred qualify_prog_constraint_list(list(prog_constraint)::in,
list(prog_constraint)::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_prog_constraint_list([], [], !Info, !Specs).
qualify_prog_constraint_list([C0 | C0s], [C | Cs], !Info, !Specs) :-
qualify_prog_constraint(C0, C, !Info, !Specs),
qualify_prog_constraint_list(C0s, Cs, !Info, !Specs).
:- pred qualify_prog_constraint(prog_constraint::in, prog_constraint::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_prog_constraint(constraint(ClassName0, Types0),
constraint(ClassName, Types), !Info, !Specs) :-
list.length(Types0, Arity),
qualify_class_name(mq_id(ClassName0, Arity), mq_id(ClassName, _),
!Info, !Specs),
qualify_type_list(Types0, Types, !Info, !Specs).
:- pred qualify_class_name(mq_id::in, mq_id::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_class_name(Class0, Class, !Info, !Specs) :-
mq_info_get_classes(!.Info, ClassIdSet),
find_unique_match(Class0, Class, ClassIdSet, class_id, !Info, !Specs).
:- pred qualify_class_interface(class_methods::in, class_methods::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_class_interface([], [], !Info, !Specs).
qualify_class_interface([M0 | M0s], [M | Ms], !Info, !Specs) :-
qualify_class_method(M0, M, !Info, !Specs),
qualify_class_interface(M0s, Ms, !Info, !Specs).
:- pred qualify_class_method(class_method::in, class_method::out,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_class_method(Method0, Method, !Info, !Specs) :-
% There is no need to qualify the method name, since that is
% done when the item is parsed.
(
Method0 = method_pred_or_func(TypeVarset, InstVarset, ExistQVars,
PredOrFunc, Name, TypesAndModes0, WithType0, WithInst0, MaybeDet,
Cond, Purity, ClassContext0, Context),
qualify_types_and_modes(TypesAndModes0, TypesAndModes, !Info, !Specs),
qualify_prog_constraints(ClassContext0, ClassContext, !Info, !Specs),
map_fold2_maybe(qualify_type, WithType0, WithType, !Info, !Specs),
map_fold2_maybe(qualify_inst, WithInst0, WithInst, !Info, !Specs),
Method = method_pred_or_func(TypeVarset, InstVarset, ExistQVars,
PredOrFunc, Name, TypesAndModes, WithType, WithInst, MaybeDet,
Cond, Purity, ClassContext, Context)
;
Method0 = method_pred_or_func_mode(Varset, PredOrFunc, Name, Modes0,
WithInst0, MaybeDet, Cond, Context),
qualify_mode_list(Modes0, Modes, !Info, !Specs),
map_fold2_maybe(qualify_inst, WithInst0, WithInst, !Info, !Specs),
Method = method_pred_or_func_mode(Varset, PredOrFunc, Name, Modes,
WithInst, MaybeDet, Cond, Context)
).
:- pred qualify_instance_body(sym_name::in,
instance_body::in, instance_body::out) is det.
qualify_instance_body(ClassName, InstanceBody0, InstanceBody) :-
(
InstanceBody0 = instance_body_abstract,
InstanceBody = instance_body_abstract
;
InstanceBody0 = instance_body_concrete(M0s),
(
ClassName = unqualified(_),
Ms = M0s
;
ClassName = qualified(_, _),
sym_name_get_module_name_default(ClassName, unqualified(""),
Module),
Qualify = (pred(M0::in, M::out) is det :-
M0 = instance_method(A, Method0, C, D, E),
add_module_qualifier(Module, Method0, Method),
M = instance_method(A, Method, C, D, E)
),
list.map(Qualify, M0s, Ms)
),
InstanceBody = instance_body_concrete(Ms)
).
:- pred add_module_qualifier(sym_name::in, sym_name::in, sym_name::out) is det.
add_module_qualifier(DefaultModule, SymName0, SymName) :-
(
SymName0 = unqualified(Name),
SymName = qualified(DefaultModule, Name)
;
SymName0 = qualified(SymModule, SubSymName),
( match_sym_name(SymModule, DefaultModule) ->
SymName = qualified(DefaultModule, SubSymName)
;
% This case is an error. The user must have written something like
% :- instance foo.bar(some_type) where [
% pred(baz.p/1) is q
% ].
% where the module qualifier on the pred or func in the instance
% (baz) does not match the qualifier for the class name (foo).
%
% We don't report the error here, we just leave the original
% module qualifier intact so that the error can be reported
% later on.
SymName = SymName0
)
).
% Find the unique match in the current name space for a given mq_id
% from a list of ids. If none exists, either because no match
% was found or multiple matches were found, report an error.
% This predicate assumes that type_ids, inst_ids, mode_ids and
% class_ids have the same representation.
%
:- pred find_unique_match(mq_id::in, mq_id::out, id_set::in, id_type::in,
mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
find_unique_match(Id0, Id, Ids, TypeOfId, !Info, !Specs) :-
% Find all IDs which match the current id.
Id0 = mq_id(SymName0, Arity),
mq_info_get_modules(!.Info, Modules),
id_set_search_sym_arity(Ids, SymName0, Arity, Modules, MatchingModules0),
( mq_info_get_import_status(!.Info, mq_status_exported) ->
% Items in the interface may only refer to modules
% imported in the interface.
mq_info_get_interface_visible_modules(!.Info, InterfaceImports),
list.filter(set.contains(InterfaceImports),
MatchingModules0, MatchingModules)
;
MatchingModules = MatchingModules0
),
(
MatchingModules = [],
% No matches for this id.
Id = Id0,
( mq_info_get_report_error_flag(!.Info, yes) ->
report_undefined(MatchingModules0, !.Info, Id0, TypeOfId, !Specs),
mq_info_set_error_flag(TypeOfId, !Info)
;
true
)
;
MatchingModules = [Module],
% A unique match for this ID.
IdName = unqualify_name(SymName0),
Id = mq_id(qualified(Module, IdName), Arity),
mq_info_set_module_used(Module, !Info),
ItemType = convert_simple_item_type(TypeOfId),
ItemName0 = item_name(SymName0, Arity),
ItemName = item_name(qualified(Module, IdName), Arity),
update_recompilation_info(
recompilation.record_used_item(ItemType, ItemName0, ItemName),
!Info)
;
MatchingModules = [_, _ | _],
% There are multiple matches.
Id = Id0,
( mq_info_get_report_error_flag(!.Info, yes) ->
mq_info_get_error_context(!.Info, ErrorContext),
report_ambiguous_match(ErrorContext, Id0, TypeOfId,
MatchingModules, !Specs),
mq_info_set_error_flag(TypeOfId, !Info)
;
true
)
).
:- pred update_recompilation_info(
pred(recompilation_info, recompilation_info)::in(pred(in, out) is det),
mq_info::in, mq_info::out) is det.
update_recompilation_info(Pred, !Info) :-
mq_info_get_recompilation_info(!.Info, MaybeRecompInfo0),
(
MaybeRecompInfo0 = yes(RecompInfo0),
Pred(RecompInfo0, RecompInfo),
mq_info_set_recompilation_info(yes(RecompInfo), !Info)
;
MaybeRecompInfo0 = no
).
:- func convert_simple_item_type(id_type) = item_type.
convert_simple_item_type(type_id) = type_abstract_item.
convert_simple_item_type(mode_id) = mode_item.
convert_simple_item_type(inst_id) = inst_item.
convert_simple_item_type(class_id) = typeclass_item.
:- pred qualify_user_sharing(user_annotated_sharing::in,
user_annotated_sharing::out, mq_info::in, mq_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
qualify_user_sharing(!UserSharing, !Info, !Specs) :-
(
!.UserSharing = no_user_annotated_sharing
;
!.UserSharing = user_sharing(Sharing, MaybeTypes0),
(
MaybeTypes0 = yes(user_type_info(Types0, TVarset)),
qualify_type_list(Types0, Types, !Info, !Specs),
MaybeTypes = yes(user_type_info(Types, TVarset)),
!:UserSharing = user_sharing(Sharing, MaybeTypes)
;
MaybeTypes0 = no
)
).
%-----------------------------------------------------------------------------%
:- type id_type
---> type_id
; mode_id
; inst_id
; class_id.
:- type error_context == pair(mq_error_context, prog_context).
:- type mq_id
---> mq_id(sym_name, int).
:- type mq_error_context
---> mqec_type(mq_id)
; mqec_inst(mq_id)
; mqec_mode(mq_id)
; mqec_pred_or_func(pred_or_func, mq_id)
; mqec_pred_or_func_mode(maybe(pred_or_func), mq_id)
; mqec_pragma(pragma_type)
; mqec_lambda_expr
; mqec_clause_mode_annotation
; mqec_type_qual
; mqec_class(mq_id)
; mqec_instance(mq_id)
; mqec_mutable(string)
; mqec_event_spec_attr(string, string). % event name, attr name
:- func id_to_sym_name_and_arity(mq_id) = sym_name_and_arity.
id_to_sym_name_and_arity(mq_id(SymName, Arity)) = SymName / Arity.
% Report an undefined type, inst or mode.
%
:- pred report_undefined(list(module_name)::in, mq_info::in, mq_id::in,
id_type::in, list(error_spec)::in, list(error_spec)::out) is det.
report_undefined(MatchingModules, Info, Id, IdType, !Specs) :-
mq_info_get_error_context(Info, ErrorContext - Context),
id_type_to_string(IdType, IdStr),
Pieces1 = [words("In")] ++ mq_error_context_to_pieces(ErrorContext) ++
[suffix(":"), nl, words("error: undefined"), fixed(IdStr),
sym_name_and_arity(id_to_sym_name_and_arity(Id)),
suffix("."), nl],
(
% If it is a qualified symbol, then check whether the specified module
% has been imported.
Id = mq_id(qualified(ModuleName, _), _Arity),
mq_info_get_imported_modules(Info, ImportedModules),
\+ set.member(ModuleName, ImportedModules),
\+ ModuleName = Info ^ this_module
->
Pieces2 = [words("(The module"), sym_name(ModuleName),
words("has not been imported.)"), nl]
;
(
MatchingModules = [_ | MatchingModulesTail],
(
MatchingModulesTail = [],
ModuleWord = "module",
HasWord = "has"
;
MatchingModulesTail = [_ | _],
ModuleWord = "modules",
HasWord = "have"
),
MatchingSymNames = list.map(wrap_module_name, MatchingModules),
Pieces2 = [words("(The"), fixed(ModuleWord)] ++
component_list_to_pieces(MatchingSymNames) ++
[fixed(HasWord),
words("not been imported in the interface.)"), nl]
;
MatchingModules = [],
Pieces2 = []
)
),
Msg = simple_msg(Context, [always(Pieces1 ++ Pieces2)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
% Report an error where a type, inst, mode or typeclass had
% multiple possible matches.
%
:- pred report_ambiguous_match(error_context::in, mq_id::in, id_type::in,
list(module_name)::in, list(error_spec)::in, list(error_spec)::out) is det.
report_ambiguous_match(ErrorContext - Context, Id, IdType, Modules, !Specs) :-
id_type_to_string(IdType, IdStr),
ModuleNames = list.map(wrap_module_name, Modules),
MainPieces = [words("In")] ++ mq_error_context_to_pieces(ErrorContext) ++
[words("ambiguity error: multiple possible matches for"),
fixed(IdStr), wrap_id(Id), suffix("."), nl,
words("The possible matches are in modules")] ++ ModuleNames ++
[suffix("."), nl],
VerbosePieces = [words("An explicit module qualifier"),
words("may be necessary."), nl],
Msg = simple_msg(Context,
[always(MainPieces), verbose_only(VerbosePieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
% Give a context for the current error message.
%
:- func mq_error_context_to_pieces(mq_error_context) = list(format_component).
mq_error_context_to_pieces(mqec_type(Id)) =
[words("definition of type"), wrap_id(Id)].
mq_error_context_to_pieces(mqec_mode(Id)) =
[words("definition of mode"), wrap_id(Id)].
mq_error_context_to_pieces(mqec_inst(Id)) =
[words("definition of inst"), wrap_id(Id)].
mq_error_context_to_pieces(mqec_pred_or_func(PredOrFunc, Id)) = Pieces :-
Id = mq_id(SymName, OrigArity),
adjust_func_arity(PredOrFunc, OrigArity, Arity),
Pieces = [words("definition of "),
fixed(pred_or_func_to_full_str(PredOrFunc)),
sym_name_and_arity(SymName / Arity)].
mq_error_context_to_pieces(mqec_pred_or_func_mode(MaybePredOrFunc, Id))
= Pieces :-
Id = mq_id(SymName, OrigArity),
(
MaybePredOrFunc = yes(PredOrFunc),
adjust_func_arity(PredOrFunc, OrigArity, Arity),
Pieces = [words("mode declaration for"),
fixed(pred_or_func_to_full_str(PredOrFunc)),
sym_name_and_arity(SymName / Arity)]
;
MaybePredOrFunc = no,
Pieces = [words("mode declaration for"),
sym_name_and_arity(SymName / OrigArity)]
).
mq_error_context_to_pieces(mqec_lambda_expr) =
[words("mode declaration for lambda expression")].
mq_error_context_to_pieces(mqec_clause_mode_annotation) =
[words("clause mode annotation")].
mq_error_context_to_pieces(mqec_pragma(Pragma)) =
[words("pragma"), words(PragmaName)] :-
(
Pragma = pragma_foreign_decl(_),
PragmaName = "foreign_decl"
;
Pragma = pragma_foreign_code(_),
PragmaName = "foreign_code"
;
Pragma = pragma_foreign_proc(_),
PragmaName = "foreign_proc"
;
Pragma = pragma_foreign_import_module(_),
PragmaName = "foreign_import_module"
;
Pragma = pragma_foreign_proc_export(_),
PragmaName = "foreign_proc_export"
;
Pragma = pragma_foreign_export_enum(_),
PragmaName = "foreign_export_enum"
;
Pragma = pragma_foreign_enum(_),
PragmaName = "foreign_enum"
;
Pragma = pragma_type_spec(_),
PragmaName = "type_spec"
;
Pragma = pragma_inline(_),
PragmaName = "inline"
;
Pragma = pragma_no_inline(_),
PragmaName = "no_inline"
;
Pragma = pragma_unused_args(_),
PragmaName = "unused_args"
;
Pragma = pragma_exceptions(_),
PragmaName = "exceptions"
;
Pragma = pragma_trailing_info(_),
PragmaName = "trailing_info"
;
Pragma = pragma_mm_tabling_info(_),
PragmaName = "mm_tabling_info"
;
Pragma = pragma_obsolete(_),
PragmaName = "obsolete"
;
Pragma = pragma_no_detism_warning(_),
PragmaName = "no_detism_warning"
;
Pragma = pragma_source_file(_),
PragmaName = "source_file"
;
Pragma = pragma_tabled(_),
PragmaName = "tabled"
;
Pragma = pragma_fact_table(_),
PragmaName = "fact_table"
;
Pragma = pragma_reserve_tag(_),
PragmaName = "reserve_tag"
;
Pragma = pragma_oisu(_),
PragmaName = "oisu"
;
Pragma = pragma_promise_eqv_clauses(_),
PragmaName = "promise_equivalent_clauses"
;
Pragma = pragma_promise_pure(_),
PragmaName = "promise_pure"
;
Pragma = pragma_promise_semipure(_),
PragmaName = "promise_semipure"
;
Pragma = pragma_termination_info(_),
PragmaName = "termination_info"
;
Pragma = pragma_termination2_info(_),
PragmaName = "termination2_info"
;
Pragma = pragma_terminates(_),
PragmaName = "terminates"
;
Pragma = pragma_does_not_terminate(_),
PragmaName = "does_not_terminate"
;
Pragma = pragma_check_termination(_),
PragmaName = "check_termination"
;
Pragma = pragma_mode_check_clauses(_),
PragmaName = "mode_check_clauses"
;
Pragma = pragma_structure_sharing(_),
PragmaName = "structure_sharing"
;
Pragma = pragma_structure_reuse(_),
PragmaName = "structure_reuse"
;
Pragma = pragma_require_feature_set(_),
PragmaName = "require_feature_set"
).
mq_error_context_to_pieces(mqec_type_qual) =
[words("explicit type qualification")].
mq_error_context_to_pieces(mqec_class(Id)) =
[words("declaration of typeclass"), wrap_id(Id)].
mq_error_context_to_pieces(mqec_instance(Id)) =
[words("declaration of instance of typeclass"), wrap_id(Id)].
mq_error_context_to_pieces(mqec_mutable(Name)) =
[words("declaration for mutable "), prefix("`"), words(Name), suffix("'")].
mq_error_context_to_pieces(mqec_event_spec_attr(EventName, AttrName)) =
[words("attribute"), quote(AttrName), words("for"), quote(EventName)].
:- pred id_type_to_string(id_type::in, string::out) is det.
id_type_to_string(type_id, "type").
id_type_to_string(mode_id, "mode").
id_type_to_string(inst_id, "inst").
id_type_to_string(class_id, "typeclass").
% Warn about modules imported in the interface when they do not need to be.
%
:- pred maybe_warn_unused_interface_imports(module_name::in, string::in,
list(module_name)::in, list(error_spec)::in, list(error_spec)::out) is det.
maybe_warn_unused_interface_imports(ModuleName, FileName, UnusedImports,
!Specs) :-
(
UnusedImports = []
;
UnusedImports = [_ | _],
term.context_init(FileName, 1, Context),
UnusedSymNames = list.map(wrap_module_name, UnusedImports),
Pieces = [words("In module"), sym_name(ModuleName), suffix(":"), nl,
words("warning:"),
words(choose_number(UnusedImports, "module", "modules"))] ++
component_list_to_pieces(UnusedSymNames) ++
[words(choose_number(UnusedImports, "is", "are")),
words("imported in the interface,"),
words("but"), words(choose_number(UnusedImports, "is", "are")),
words("not used in the interface.")],
Msg = simple_msg(Context,
[option_is_set(warn_interface_imports, yes, [always(Pieces)])]),
Severity = severity_conditional(warn_interface_imports, yes,
severity_warning, no),
Spec = error_spec(Severity, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs]
).
:- func wrap_module_name(module_name) = format_component.
wrap_module_name(SymName) = sym_name(SymName).
:- func wrap_id(mq_id) = format_component.
wrap_id(mq_id(Name, Arity)) = sym_name_and_arity(Name / Arity).
% Output an error message about an ill-formed user_inst.
%
:- pred report_invalid_user_inst(sym_name::in, list(mer_inst)::in,
error_context::in, list(error_spec)::in, list(error_spec)::out) is det.
report_invalid_user_inst(_SymName, _Insts, ErrorContext - Context, !Specs) :-
ContextPieces = mq_error_context_to_pieces(ErrorContext),
Pieces = [words("In")] ++ ContextPieces ++ [suffix(":"), nl,
words("error: variable used as inst constructor."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
%-----------------------------------------------------------------------------%
% is_builtin_atomic_type(TypeCtor):
%
% Succeeds iff 'TypeCtor' is the type_ctor of a builtin atomic type.
%
:- pred is_builtin_atomic_type(type_ctor::in) is semidet.
is_builtin_atomic_type(type_ctor(unqualified("int"), 0)).
is_builtin_atomic_type(type_ctor(unqualified("float"), 0)).
is_builtin_atomic_type(type_ctor(unqualified("string"), 0)).
is_builtin_atomic_type(type_ctor(unqualified("character"), 0)).
%-----------------------------------------------------------------------------%
%
% Access and initialisation predicates.
%
:- pred init_mq_info(list(item)::in, globals::in, bool::in, module_name::in,
mq_info::out) is det.
init_mq_info(Items, Globals, ReportErrors, ModuleName, Info) :-
term.context_init(Context),
ErrorContext = mqec_type(mq_id(unqualified(""), 0)) - Context,
set.init(InterfaceModules0),
set.init(InstanceModules),
ExportedInstancesFlag = no,
get_implicit_dependencies(Items, Globals, ImportDeps, UseDeps),
set.list_to_set(ImportDeps `list.append` UseDeps, ImportedModules),
% Ancestor modules are visible without being explicitly imported.
set.insert_list([ModuleName | get_ancestors(ModuleName)],
ImportedModules, InterfaceVisibleModules),
id_set_init(Empty),
globals.lookup_bool_option(Globals, smart_recompilation,
SmartRecompilation),
(
SmartRecompilation = no,
MaybeRecompInfo = no
;
SmartRecompilation = yes,
MaybeRecompInfo = yes(init_recompilation_info(ModuleName))
),
Info = mq_info(ImportedModules, InterfaceVisibleModules,
Empty, Empty, Empty, Empty, Empty, Empty,
InterfaceModules0, InstanceModules, ExportedInstancesFlag,
mq_status_local, 0, no, no, ReportErrors, ErrorContext, ModuleName,
may_be_unqualified, MaybeRecompInfo).
:- pred mq_info_get_imported_modules(mq_info::in, set(module_name)::out)
is det.
:- pred mq_info_get_interface_visible_modules(mq_info::in,
set(module_name)::out) is det.
:- pred mq_info_get_modules(mq_info::in, module_id_set::out) is det.
:- pred mq_info_get_types(mq_info::in, type_id_set::out) is det.
:- pred mq_info_get_impl_types(mq_info::in, type_id_set::out) is det.
:- pred mq_info_get_insts(mq_info::in, inst_id_set::out) is det.
:- pred mq_info_get_modes(mq_info::in, mode_id_set::out) is det.
:- pred mq_info_get_classes(mq_info::in, class_id_set::out) is det.
:- pred mq_info_get_unused_interface_modules(mq_info::in,
set(module_name)::out) is det.
:- pred mq_info_get_imported_instance_modules(mq_info::in,
set(module_name)::out) is det.
:- pred mq_info_get_exported_instances_flag(mq_info::in, bool::out) is det.
:- pred mq_info_get_import_status(mq_info::in, mq_import_status::out) is det.
% :- pred mq_info_get_type_error_flag(mq_info::in, bool::out) is det.
% :- pred mq_info_get_mode_error_flag(mq_info::in, bool::out) is det.
:- pred mq_info_get_report_error_flag(mq_info::in, bool::out) is det.
:- pred mq_info_get_error_context(mq_info::in, error_context::out) is det.
mq_info_get_imported_modules(Info, Info ^ imported_modules).
mq_info_get_interface_visible_modules(Info, Info ^ interface_visible_modules).
mq_info_get_modules(Info, Info ^ modules).
mq_info_get_types(Info, Info ^ types).
mq_info_get_impl_types(Info, Info ^ impl_types).
mq_info_get_insts(Info, Info ^ insts).
mq_info_get_modes(Info, Info ^ modes).
mq_info_get_classes(Info, Info ^ classes).
mq_info_get_unused_interface_modules(Info, Info ^ unused_interface_modules).
mq_info_get_imported_instance_modules(Info, Info ^ imported_instance_modules).
mq_info_get_exported_instances_flag(Info, Info ^ exported_instances_flag).
mq_info_get_import_status(Info, Info ^ import_status).
mq_info_get_type_error_flag(Info, Info ^ type_error_flag).
mq_info_get_mode_error_flag(Info, Info ^ mode_error_flag).
mq_info_get_report_error_flag(Info, Info ^ report_error_flag).
mq_info_get_error_context(Info, Info ^ error_context).
mq_info_get_need_qual_flag(Info, Info ^ need_qual_flag).
mq_info_get_recompilation_info(Info, Info ^ maybe_recompilation_info).
:- pred mq_info_set_imported_modules(set(module_name)::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_interface_visible_modules(set(module_name)::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_modules(module_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_types(type_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_impl_types(type_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_insts(inst_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_modes(mode_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_classes(class_id_set::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_unused_interface_modules(set(module_name)::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_imported_instance_modules(set(module_name)::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_exported_instances_flag(bool::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_import_status(mq_import_status::in,
mq_info::in, mq_info::out) is det.
:- pred mq_info_set_type_error_flag(mq_info::in, mq_info::out) is det.
:- pred mq_info_set_mode_error_flag(mq_info::in, mq_info::out) is det.
:- pred mq_info_set_error_context(error_context::in,
mq_info::in, mq_info::out) is det.
mq_info_set_imported_modules(ImportedModules, Info,
Info ^ imported_modules := ImportedModules).
mq_info_set_interface_visible_modules(ImportedModules, Info,
Info ^ interface_visible_modules := ImportedModules).
mq_info_set_modules(Modules, Info, Info ^ modules := Modules).
mq_info_set_types(Types, Info, Info ^ types := Types).
mq_info_set_impl_types(Types, Info, Info ^ impl_types := Types).
mq_info_set_insts(Insts, Info, Info ^ insts := Insts).
mq_info_set_modes(Modes, Info, Info ^ modes := Modes).
mq_info_set_classes(Classes, Info, Info ^ classes := Classes).
mq_info_set_unused_interface_modules(Modules, Info,
Info ^ unused_interface_modules := Modules).
mq_info_set_imported_instance_modules(Modules, Info,
Info ^ imported_instance_modules := Modules).
mq_info_set_exported_instances_flag(Flag, Info,
Info ^ exported_instances_flag := Flag).
mq_info_set_import_status(Status, Info, Info ^ import_status := Status).
mq_info_set_type_error_flag(Info, Info ^ type_error_flag := yes).
mq_info_set_mode_error_flag(Info, Info ^ mode_error_flag := yes).
mq_info_set_error_context(Context, Info, Info ^ error_context := Context).
mq_info_set_need_qual_flag(Flag, Info, Info ^ need_qual_flag := Flag).
mq_info_set_recompilation_info(RecompInfo, Info,
Info ^ maybe_recompilation_info := RecompInfo).
:- pred mq_info_set_error_flag(id_type::in, mq_info::in, mq_info::out) is det.
mq_info_set_error_flag(IdType, !Info) :-
mq_info_set_error_flag_2(IdType, !Info).
:- pred mq_info_set_error_flag_2(id_type::in, mq_info::in, mq_info::out)
is det.
mq_info_set_error_flag_2(type_id, !Info) :-
mq_info_set_type_error_flag(!Info).
mq_info_set_error_flag_2(mode_id, !Info) :-
mq_info_set_mode_error_flag(!Info).
mq_info_set_error_flag_2(inst_id, !Info) :-
mq_info_set_mode_error_flag(!Info).
mq_info_set_error_flag_2(class_id, !Info) :-
mq_info_set_type_error_flag(!Info).
% If the current item is in the interface, remove its module name
% from the list of modules not used in the interface (and if the
% module name is itself module-qualified, recursively mark its
% parent module as used).
%
:- pred mq_info_set_module_used(module_name::in, mq_info::in, mq_info::out)
is det.
mq_info_set_module_used(Module, !Info) :-
( mq_info_get_import_status(!.Info, mq_status_exported) ->
mq_info_get_unused_interface_modules(!.Info, Modules0),
set.delete(Module, Modules0, Modules),
mq_info_set_unused_interface_modules(Modules, !Info),
(
Module = qualified(ParentModule, _),
mq_info_set_module_used(ParentModule, !Info)
;
Module = unqualified(_)
)
;
true
).
%----------------------------------------------------------------------------%
% Define a type for representing sets of ids during module qualification
% to allow efficient retrieval of all the modules which define an id
% with a certain name and arity.
% The first set of module_names can be used without module qualifiers,
% items from the second set can only be used with module qualifiers.
% Items from modules imported with a :- use_module declaration and from `.opt'
% files should go into the second set.
:- type id_set == map(pair(string, arity), pair(set(module_name))).
:- type type_id_set == id_set.
:- type mode_id_set == id_set.
:- type inst_id_set == id_set.
:- type class_id_set == id_set.
% Modules don't have an arity, but for simplicity we use the same
% data structure here, assigning arity zero to all module names.
:- type module_id_set == id_set.
:- pred id_set_init(id_set::out) is det.
id_set_init(IdSet) :-
map.init(IdSet).
% Insert an mq_id into an id_set, aborting with an error if the
% mq_id is not module qualified.
%
:- pred id_set_insert(need_qualifier::in, mq_id::in, id_set::in, id_set::out)
is det.
id_set_insert(_, mq_id(unqualified(_), _), _, _) :-
unexpected($module, $pred, "unqualified id").
id_set_insert(NeedQualifier, mq_id(qualified(Module, Name), Arity), !IdSet) :-
( map.search(!.IdSet, Name - Arity, ImportModules0 - UseModules0) ->
ImportModules1 = ImportModules0,
UseModules1 = UseModules0
;
set.init(ImportModules1),
set.init(UseModules1)
),
(
NeedQualifier = must_be_qualified,
set.insert(Module, UseModules1, UseModules),
ImportModules = ImportModules1
;
NeedQualifier = may_be_unqualified,
set.insert(Module, ImportModules1, ImportModules),
UseModules = UseModules1
),
map.set(Name - Arity, ImportModules - UseModules, !IdSet).
:- pred id_set_search_sym_arity(id_set::in, sym_name::in, int::in,
module_id_set::in, list(module_name)::out) is det.
id_set_search_sym_arity(IdSet, Sym, Arity, Modules, MatchingModules) :-
UnqualName = unqualify_name(Sym),
(
map.search(IdSet, UnqualName - Arity, ImportModules - UseModules)
->
(
Sym = unqualified(_),
set.to_sorted_list(ImportModules, MatchingModules)
;
Sym = qualified(Module, _),
%
% First, compute the set of modules that this
% module specifier could possibly refer to.
%
% Do a recursive search to find nested modules
% that match the specified module name.
%
ModuleArity = 0,
id_set_search_sym_arity(Modules, Module, ModuleArity,
Modules, MatchingParentModules),
UnqualModule = unqualify_name(Module),
AppendModuleName = (pred(X::in, Y::out) is det :-
Y = qualified(X, UnqualModule)
),
list.map(AppendModuleName,
MatchingParentModules,
MatchingNestedModules),
%
% Add the specified module name itself, in case
% it refers to a top-level (unnested) module name,
% since top-level modules don't get inserted into
% the module_id_set.
%
AllMatchingModules = [Module | MatchingNestedModules],
%
% Second, compute the set of modules that define this symbol.
%
set.union(ImportModules, UseModules, DefiningModules),
%
% Third, take the intersection of the sets computed in
% the first two steps
%
FindMatch = (pred(MatchModule::out) is nondet :-
list.member(MatchModule, AllMatchingModules),
set.member(MatchModule, DefiningModules)
),
solutions(FindMatch, MatchingModules)
)
;
MatchingModules = []
).
%-----------------------------------------------------------------------------%
get_partial_qualifiers(ModuleName, PartialQualInfo, PartialQualifiers) :-
PartialQualInfo = partial_qualifier_info(ModuleIdSet),
(
ModuleName = unqualified(_),
PartialQualifiers = []
;
ModuleName = qualified(Parent, Child),
get_partial_qualifiers_2(Parent, unqualified(Child),
ModuleIdSet, [], PartialQualifiers)
).
:- pred get_partial_qualifiers_2(module_name::in, module_name::in,
module_id_set::in, list(module_name)::in, list(module_name)::out)
is det.
get_partial_qualifiers_2(ImplicitPart, ExplicitPart, ModuleIdSet,
!Qualifiers) :-
% If the ImplicitPart module was imported, rather than just being
% used, then insert the ExplicitPart module into the list of
% valid partial qualifiers.
( parent_module_is_imported(ImplicitPart, ExplicitPart, ModuleIdSet) ->
!:Qualifiers = [ExplicitPart | !.Qualifiers]
;
true
),
% Recursively try to add the other possible partial qualifiers.
(
ImplicitPart = qualified(Parent, Child),
NextImplicitPart = Parent,
NextExplicitPart = add_outermost_qualifier(Child, ExplicitPart),
get_partial_qualifiers_2(NextImplicitPart, NextExplicitPart,
ModuleIdSet, !Qualifiers)
;
ImplicitPart = unqualified(_)
).
% Check whether the parent module was imported, given the name of a
% child (or grandchild, etc.) module occurring in that parent module.
%
:- pred parent_module_is_imported(module_name::in, module_name::in,
module_id_set::in) is semidet.
parent_module_is_imported(ParentModule, ChildModule, ModuleIdSet) :-
% Find the module name at the start of the ChildModule;
% this sub-module will be a direct sub-module of ParentModule
DirectSubModuleName = get_first_module_name(ChildModule),
% Check that the ParentModule was imported.
% We do this by looking up the definitions for the direct sub-module
% and checking that the one in ParentModule came from an
% imported module.
Arity = 0,
map.search(ModuleIdSet, DirectSubModuleName - Arity,
ImportModules - _UseModules),
set.member(ParentModule, ImportModules).
% Given a module name, possibly module-qualified,
% return the name of the first module in the qualifier list.
% e.g. given `foo.bar.baz', this returns `foo',
% and given just `baz', it returns `baz'.
%
:- func get_first_module_name(module_name) = string.
get_first_module_name(unqualified(ModuleName)) = ModuleName.
get_first_module_name(qualified(Parent, _)) = get_first_module_name(Parent).
%----------------------------------------------------------------------------%
:- end_module parse_tree.module_qual.
%----------------------------------------------------------------------------%