mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 21:04:00 +00:00
Estimated hours taken: 100
Branches: main
Make definitions of abstract types available when generating
code for importing modules. This is necessary for the .NET
back-end, and for `:- pragma export' on the C back-end.
compiler/prog_data.m:
compiler/modules.m:
compiler/make.dependencies.m:
compiler/recompilation.version.m:
Handle implementation sections in interface files.
There is a new pseudo-declaration `abstract_imported'
which is applied to items from the implementation
section of an interface file. `abstract_imported'
items may not be used in the error checking passes
for the curent module.
compiler/equiv_type_hlds.m:
compiler/notes/compiler_design.html:
New file.
Go over the HLDS expanding all types fully after
semantic checking has been run.
compiler/mercury_compile.m:
Add the new pass.
Don't write the `.opt' file if there are any errors.
compiler/instmap.m:
Add a predicate instmap_delta_map_foldl to apply
a procedure to all insts in an instmap.
compiler/equiv_type.m:
Export predicates for use by equiv_type_hlds.m
Reorder arguments so state variables and higher-order
programming can be used.
compiler/prog_data.m:
compiler/prog_io_pragma.m:
compiler/make_hlds.m:
compiler/mercury_to_mercury.m:
Handle `:- pragma foreign_type' as a form of type
declaration rather than a pragma.
compiler/hlds_data.m:
compiler/*.m:
Add a field to the type_info_cell_constructor cons_id
to identify the type_ctor, which is needed by
equiv_type_hlds.m.
compiler/module_qual.m:
Donn't allow items from the implementation section of
interface files to match items in the current module.
compiler/*.m:
tests/*/*.m:
Add missing imports which only became apparent with
the bug fixes above.
Remove unnecessary imports which only became apparent with
the bug fixes above.
tests/hard_coded/Mmakefile:
tests/hard_coded/export_test2.{m,exp}:
Test case.
tests/invalid/Mmakefile:
tests/invalid/missing_interface_import2.{m,err_exp}:
Test case.
1144 lines
39 KiB
Mathematica
1144 lines
39 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2003 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: recompilation_version.m
|
|
% Main author: stayl
|
|
%
|
|
% Compute version numbers for program items in interface files.
|
|
%-----------------------------------------------------------------------------%
|
|
:- module recompilation__version.
|
|
|
|
:- interface.
|
|
|
|
:- import_module libs__timestamp.
|
|
:- import_module parse_tree__prog_data.
|
|
:- import_module parse_tree__prog_io_util.
|
|
|
|
:- import_module io, std_util, term.
|
|
|
|
% recompilation__version__compute_version_numbers(SourceFileModTime,
|
|
% NewItems, MaybeOldItems, VersionNumbers).
|
|
:- pred recompilation__version__compute_version_numbers(timestamp::in,
|
|
item_list::in, maybe(item_list)::in, version_numbers::out) is det.
|
|
|
|
:- pred recompilation__version__write_version_numbers(version_numbers::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
:- pred recompilation__version__parse_version_numbers(term::in,
|
|
maybe1(version_numbers)::out) is det.
|
|
|
|
% The version number for the format of the version numbers
|
|
% written to the interface files.
|
|
:- func version_numbers_version_number = int.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds__mode_util.
|
|
:- import_module check_hlds__type_util.
|
|
:- import_module hlds__hlds_out.
|
|
:- import_module parse_tree__inst.
|
|
:- import_module parse_tree__mercury_to_mercury.
|
|
:- import_module parse_tree__prog_io.
|
|
:- import_module parse_tree__prog_util.
|
|
|
|
:- import_module assoc_list, bool, list, map, require, string, varset.
|
|
|
|
|
|
recompilation__version__compute_version_numbers(SourceFileTime, Items,
|
|
MaybeOldItems,
|
|
version_numbers(ItemVersionNumbers, InstanceVersionNumbers)) :-
|
|
recompilation__version__gather_items(implementation, Items,
|
|
GatheredItems, InstanceItems),
|
|
(
|
|
MaybeOldItems = yes(OldItems0),
|
|
OldItems0 = [FirstItem, VersionNumberItem | OldItems],
|
|
FirstItem = module_defn(_, interface) - _,
|
|
VersionNumberItem = module_defn(_,
|
|
version_numbers(_, OldVersionNumbers)) - _
|
|
->
|
|
OldVersionNumbers = version_numbers(OldItemVersionNumbers,
|
|
OldInstanceVersionNumbers),
|
|
recompilation__version__gather_items(implementation,
|
|
OldItems, GatheredOldItems, OldInstanceItems)
|
|
;
|
|
% There were no old version numbers, so every item
|
|
% gets the same timestamp as the source module.
|
|
OldItemVersionNumbers = init_item_id_set(map__init),
|
|
GatheredOldItems = init_item_id_set(map__init),
|
|
map__init(OldInstanceItems),
|
|
map__init(OldInstanceVersionNumbers)
|
|
),
|
|
|
|
recompilation__version__compute_item_version_numbers(SourceFileTime,
|
|
GatheredItems, GatheredOldItems, OldItemVersionNumbers,
|
|
ItemVersionNumbers),
|
|
|
|
recompilation__version__compute_instance_version_numbers(SourceFileTime,
|
|
InstanceItems, OldInstanceItems, OldInstanceVersionNumbers,
|
|
InstanceVersionNumbers).
|
|
|
|
:- pred recompilation__version__compute_item_version_numbers(timestamp::in,
|
|
gathered_items::in, gathered_items::in,
|
|
item_version_numbers::in, item_version_numbers::out) is det.
|
|
|
|
recompilation__version__compute_item_version_numbers(SourceFileTime,
|
|
GatheredItems, GatheredOldItems,
|
|
OldVersionNumbers, VersionNumbers) :-
|
|
VersionNumbers = map_ids(
|
|
(func(ItemType, Items0) =
|
|
map__map_values(
|
|
(func(NameArity, Items) = VersionNumber :-
|
|
OldIds = extract_ids(GatheredOldItems, ItemType),
|
|
(
|
|
map__search(OldIds, NameArity, OldItems),
|
|
items_are_unchanged(OldItems, Items),
|
|
map__search(
|
|
extract_ids(OldVersionNumbers, ItemType),
|
|
NameArity, OldVersionNumber)
|
|
->
|
|
VersionNumber = OldVersionNumber
|
|
;
|
|
VersionNumber = SourceFileTime
|
|
)
|
|
),
|
|
Items0
|
|
)
|
|
),
|
|
GatheredItems,
|
|
map__init
|
|
).
|
|
|
|
:- pred recompilation__version__compute_instance_version_numbers(timestamp::in,
|
|
instance_item_map::in, instance_item_map::in,
|
|
instance_version_numbers::in, instance_version_numbers::out) is det.
|
|
|
|
recompilation__version__compute_instance_version_numbers(SourceFileTime,
|
|
InstanceItems, OldInstanceItems,
|
|
OldInstanceVersionNumbers, InstanceVersionNumbers) :-
|
|
InstanceVersionNumbers =
|
|
map__map_values(
|
|
(func(ClassId, Items) = VersionNumber :-
|
|
(
|
|
map__search(OldInstanceItems, ClassId, OldItems),
|
|
items_are_unchanged(OldItems, Items),
|
|
map__search(OldInstanceVersionNumbers, ClassId,
|
|
OldVersionNumber)
|
|
->
|
|
VersionNumber = OldVersionNumber
|
|
;
|
|
VersionNumber = SourceFileTime
|
|
)
|
|
),
|
|
InstanceItems
|
|
).
|
|
|
|
:- pred recompilation__version__gather_items(section::in, item_list::in,
|
|
gathered_items::out, instance_item_map::out) is det.
|
|
|
|
recompilation__version__gather_items(Section,
|
|
Items, GatheredItems, Instances) :-
|
|
list__reverse(Items, RevItems),
|
|
Info0 = gathered_item_info(init_item_id_set(map__init),
|
|
[], [], map__init),
|
|
list__foldl2(recompilation__version__gather_items_2, RevItems,
|
|
Section, _, Info0, Info1),
|
|
|
|
%
|
|
% Items which could appear in _OtherItems (those which aren't
|
|
% gathered into the list for another type of item) can't appear
|
|
% in the interface section. Those other items (e.g. assertions)
|
|
% will need to be handled here when smart recompilation is made to
|
|
% work with `--intermodule-optimization'.
|
|
%
|
|
Info1 = gathered_item_info(GatheredItems1, PragmaItems,
|
|
_OtherItems, Instances),
|
|
list__reverse(PragmaItems, RevPragmaItems),
|
|
list__foldl(distribute_pragma_items, RevPragmaItems,
|
|
GatheredItems1, GatheredItems).
|
|
|
|
:- pred distribute_pragma_items(
|
|
{maybe_pred_or_func_id, item_and_context, section}::in,
|
|
gathered_items::in, gathered_items::out) is det.
|
|
|
|
distribute_pragma_items({ItemId, ItemAndContext, Section},
|
|
GatheredItems0, GatheredItems) :-
|
|
ItemId = MaybePredOrFunc - SymName / Arity,
|
|
ItemAndContext = Item - ItemContext,
|
|
|
|
% For predicates defined using `with_type` annotations
|
|
% we don't know the actual arity, so always we need to add
|
|
% entries for pragmas, even if the pragma doesn't match any
|
|
% recorded predicate. For pragmas which don't include enough
|
|
% information to work out whether they apply to a predicate
|
|
% or a function this will result in an extra entry in the
|
|
% version numbers. Pragmas in the interface aren't common
|
|
% so this won't be too much of a problem.
|
|
AddIfNotExisting = yes,
|
|
(
|
|
MaybePredOrFunc = yes(PredOrFunc),
|
|
ItemType = pred_or_func_to_item_type(PredOrFunc),
|
|
recompilation__version__add_gathered_item(Item,
|
|
item_id(ItemType, SymName - Arity),
|
|
ItemContext, Section, AddIfNotExisting,
|
|
GatheredItems0, GatheredItems2)
|
|
;
|
|
MaybePredOrFunc = no,
|
|
recompilation__version__add_gathered_item(Item,
|
|
item_id(predicate, SymName - Arity),
|
|
ItemContext, Section, AddIfNotExisting,
|
|
GatheredItems0, GatheredItems1),
|
|
recompilation__version__add_gathered_item(Item,
|
|
item_id(function, SymName - Arity),
|
|
ItemContext, Section, AddIfNotExisting,
|
|
GatheredItems1, GatheredItems2)
|
|
),
|
|
|
|
% Pragmas can apply to typeclass methods.
|
|
map__map_values(
|
|
(pred(_::in, ClassItems0::in, ClassItems::out) is det :-
|
|
(
|
|
% Does this pragma match any of the methods
|
|
% of this class.
|
|
list__member(_ - ClassItem, ClassItems0),
|
|
ClassItem = typeclass(_, _, _, Interface, _) - _,
|
|
Interface = concrete(Methods),
|
|
list__member(Method, Methods),
|
|
Method = pred_or_func(_, _, _, MethodPredOrFunc,
|
|
SymName, TypesAndModes, WithType, _,
|
|
_, _, _, _, _),
|
|
( MaybePredOrFunc = yes(MethodPredOrFunc)
|
|
; MaybePredOrFunc = no
|
|
),
|
|
(
|
|
WithType = no,
|
|
adjust_func_arity(MethodPredOrFunc,
|
|
Arity, list__length(TypesAndModes))
|
|
;
|
|
% We don't know the actual arity, so just
|
|
% match on the name and pred_or_func.
|
|
WithType = yes(_)
|
|
)
|
|
->
|
|
% XXX O(N^2), but shouldn't happen too often.
|
|
ClassItems = ClassItems0 ++ [Section - ItemAndContext]
|
|
;
|
|
ClassItems = ClassItems0
|
|
)
|
|
), extract_ids(GatheredItems2, typeclass), GatheredTypeClasses),
|
|
GatheredItems = update_ids(GatheredItems2, typeclass,
|
|
GatheredTypeClasses).
|
|
|
|
:- type gathered_item_info
|
|
---> gathered_item_info(
|
|
gathered_items :: gathered_items,
|
|
pragma_items :: list({maybe_pred_or_func_id,
|
|
item_and_context, section}),
|
|
other_items :: item_list,
|
|
instances :: instance_item_map
|
|
).
|
|
|
|
:- type instance_item_map ==
|
|
map(item_name, assoc_list(section, item_and_context)).
|
|
|
|
% The constructors set should always be empty.
|
|
:- type gathered_items == item_id_set(gathered_item_map).
|
|
:- type gathered_item_map == map(pair(string, arity),
|
|
assoc_list(section, item_and_context)).
|
|
|
|
:- pred recompilation__version__gather_items_2(item_and_context::in,
|
|
section::in, section::out,
|
|
gathered_item_info::in, gathered_item_info::out) is det.
|
|
|
|
recompilation__version__gather_items_2(ItemAndContext, !Section) -->
|
|
{ ItemAndContext = Item - ItemContext },
|
|
(
|
|
{ Item = module_defn(_, interface) }
|
|
->
|
|
{ !:Section = interface }
|
|
;
|
|
{ Item = module_defn(_, implementation) }
|
|
->
|
|
{ !:Section = implementation }
|
|
;
|
|
{ Item = type_defn(VarSet, Name, Args, Body, Cond) }
|
|
->
|
|
(
|
|
{ Body = abstract_type(_) },
|
|
{ NameItem = Item },
|
|
% The body of an abstract type can be recorded
|
|
% as used when generating a call to the automatically
|
|
% generated unification procedure.
|
|
{ BodyItem = Item }
|
|
;
|
|
{ Body = du_type(_, IsSolverType, _) },
|
|
{ NameItem = type_defn(VarSet, Name, Args,
|
|
abstract_type(IsSolverType), Cond) },
|
|
{ BodyItem = Item }
|
|
;
|
|
{ Body = eqv_type(_) },
|
|
% When we use an equivalence type we
|
|
% always use the body.
|
|
{ NameItem = Item },
|
|
{ BodyItem = Item }
|
|
;
|
|
{ Body = foreign_type(_, _) },
|
|
{ NameItem = Item },
|
|
{ BodyItem = Item }
|
|
),
|
|
{ TypeCtor = Name - list__length(Args) },
|
|
GatheredItems0 =^ gathered_items,
|
|
{ recompilation__version__add_gathered_item(NameItem,
|
|
item_id((type), TypeCtor), ItemContext, !.Section,
|
|
yes, GatheredItems0, GatheredItems1) },
|
|
{ recompilation__version__add_gathered_item(BodyItem,
|
|
item_id(type_body, TypeCtor), ItemContext, !.Section,
|
|
yes, GatheredItems1, GatheredItems) },
|
|
^ gathered_items := GatheredItems
|
|
;
|
|
{ Item = instance(_, ClassName, ClassArgs, _, _, _) }
|
|
->
|
|
Instances0 =^ instances,
|
|
{ ClassArity = list__length(ClassArgs) },
|
|
(
|
|
{ map__search(Instances0, ClassName - ClassArity,
|
|
InstanceItems0) }
|
|
->
|
|
{ InstanceItems = InstanceItems0 }
|
|
;
|
|
{ InstanceItems = [] }
|
|
),
|
|
{ map__set(Instances0, ClassName - ClassArity,
|
|
[!.Section - (Item - ItemContext) | InstanceItems],
|
|
Instances) },
|
|
^ instances := Instances
|
|
;
|
|
% For predicates or functions defined using `with_inst`
|
|
% annotations the pred_or_func and arity here won't be
|
|
% correct, but equiv_type.m will record the dependency
|
|
% on the version number with the `incorrect' pred_or_func
|
|
% and arity, so this will work.
|
|
{ Item = pred_or_func_mode(_, MaybePredOrFunc,
|
|
SymName, Modes, WithInst, _, _) },
|
|
{ MaybePredOrFunc = no },
|
|
{ WithInst = yes(_) }
|
|
->
|
|
GatheredItems0 =^ gathered_items,
|
|
{ ItemName = SymName - list__length(Modes) },
|
|
{ recompilation__version__add_gathered_item(Item,
|
|
item_id(predicate, ItemName), ItemContext, !.Section,
|
|
yes, GatheredItems0, GatheredItems1) },
|
|
{ recompilation__version__add_gathered_item(Item,
|
|
item_id(function, ItemName), ItemContext,
|
|
!.Section, yes, GatheredItems1, GatheredItems) },
|
|
^ gathered_items := GatheredItems
|
|
;
|
|
|
|
{ item_to_item_id(Item, ItemId) }
|
|
->
|
|
GatheredItems0 =^ gathered_items,
|
|
{ recompilation__version__add_gathered_item(Item, ItemId,
|
|
ItemContext, !.Section, yes,
|
|
GatheredItems0, GatheredItems) },
|
|
^ gathered_items := GatheredItems
|
|
;
|
|
{ Item = pragma(PragmaType) },
|
|
{ is_pred_pragma(PragmaType, yes(PredOrFuncId)) }
|
|
->
|
|
PragmaItems =^ pragma_items,
|
|
^ pragma_items :=
|
|
[{PredOrFuncId, ItemAndContext, !.Section} | PragmaItems]
|
|
;
|
|
OtherItems =^ other_items,
|
|
^ other_items := [ItemAndContext | OtherItems]
|
|
).
|
|
|
|
:- pred recompilation__version__add_gathered_item(item::in, item_id::in,
|
|
prog_context::in, section::in, bool::in, gathered_items::in,
|
|
gathered_items::out) is det.
|
|
|
|
recompilation__version__add_gathered_item(Item, ItemId, ItemContext,
|
|
Section, AddIfNotExisting, GatheredItems0, GatheredItems) :-
|
|
ItemId = item_id(ItemType, Id),
|
|
Id = SymName - Arity,
|
|
unqualify_name(SymName, Name),
|
|
IdMap0 = extract_ids(GatheredItems0, ItemType),
|
|
NameArity = Name - Arity,
|
|
( map__search(IdMap0, NameArity, MatchingItems0) ->
|
|
MatchingItems = MatchingItems0
|
|
;
|
|
MatchingItems = []
|
|
),
|
|
( MatchingItems = [], AddIfNotExisting = no ->
|
|
GatheredItems = GatheredItems0
|
|
;
|
|
recompilation__version__add_gathered_item_2(Item, ItemType,
|
|
NameArity, ItemContext, Section, MatchingItems,
|
|
GatheredItems0, GatheredItems)
|
|
).
|
|
|
|
:- pred recompilation__version__add_gathered_item_2(item::in, item_type::in,
|
|
pair(string, arity)::in, prog_context::in, section::in,
|
|
assoc_list(section, item_and_context)::in,
|
|
gathered_items::in, gathered_items::out) is det.
|
|
|
|
recompilation__version__add_gathered_item_2(Item, ItemType, NameArity,
|
|
ItemContext, Section, MatchingItems0,
|
|
GatheredItems0, GatheredItems) :-
|
|
|
|
% mercury_to_mercury.m splits combined pred and mode
|
|
% declarations. That needs to be done here as well
|
|
% the item list read from the interface file will match
|
|
% the item list generated here.
|
|
(
|
|
Item = pred_or_func(TVarSet, InstVarSet, ExistQVars,
|
|
PredOrFunc, PredName, TypesAndModes, WithType,
|
|
WithInst, Det, Cond, Purity, ClassContext),
|
|
split_types_and_modes(TypesAndModes, Types, MaybeModes),
|
|
MaybeModes = yes(Modes),
|
|
( Modes \= []
|
|
; WithInst = yes(_)
|
|
)
|
|
->
|
|
TypesWithoutModes = list__map(
|
|
(func(Type) = type_only(Type)), Types),
|
|
varset__init(EmptyInstVarSet),
|
|
PredOrFuncItem = pred_or_func(TVarSet, EmptyInstVarSet,
|
|
ExistQVars, PredOrFunc, PredName, TypesWithoutModes,
|
|
WithType, no, no, Cond, Purity, ClassContext),
|
|
(
|
|
WithInst = yes(_),
|
|
% MaybePredOrFunc needs to be `no' here because when
|
|
% the item is read from the interface file we won't
|
|
% know whether it is a predicate or a function mode.
|
|
MaybePredOrFunc = no
|
|
;
|
|
WithInst = no,
|
|
MaybePredOrFunc = yes(PredOrFunc)
|
|
),
|
|
PredOrFuncModeItem = pred_or_func_mode(InstVarSet,
|
|
MaybePredOrFunc, PredName, Modes, WithInst, Det, Cond),
|
|
MatchingItems =
|
|
[Section - (PredOrFuncItem - ItemContext),
|
|
Section - (PredOrFuncModeItem - ItemContext)
|
|
| MatchingItems0]
|
|
;
|
|
Item = typeclass(Constraints, ClassName, ClassArgs,
|
|
ClassInterface0, ClassTVarSet),
|
|
ClassInterface0 = concrete(Methods0)
|
|
->
|
|
MethodsList = list__map(
|
|
split_class_method_types_and_modes, Methods0),
|
|
list__condense(MethodsList, Methods),
|
|
TypeclassItem = typeclass(Constraints, ClassName, ClassArgs,
|
|
concrete(Methods), ClassTVarSet),
|
|
MatchingItems = [Section - (TypeclassItem - ItemContext)
|
|
| MatchingItems0]
|
|
;
|
|
MatchingItems = [Section - (Item - ItemContext)
|
|
| MatchingItems0]
|
|
),
|
|
|
|
IdMap0 = extract_ids(GatheredItems0, ItemType),
|
|
map__set(IdMap0, NameArity, MatchingItems, IdMap),
|
|
GatheredItems = update_ids(GatheredItems0, ItemType, IdMap).
|
|
|
|
:- func split_class_method_types_and_modes(class_method) = list(class_method).
|
|
|
|
split_class_method_types_and_modes(Method0) = Items :-
|
|
% Always strip the context from the item -- this is needed
|
|
% so the items can be easily tested for equality.
|
|
Method0 = pred_or_func(TVarSet, InstVarSet, ExistQVars,
|
|
PredOrFunc, SymName, TypesAndModes, WithType, WithInst,
|
|
MaybeDet, Cond, Purity, ClassContext, _),
|
|
(
|
|
split_types_and_modes(TypesAndModes, Types, MaybeModes),
|
|
MaybeModes = yes(Modes),
|
|
( Modes \= []
|
|
; WithInst = yes(_)
|
|
)
|
|
->
|
|
TypesWithoutModes = list__map(
|
|
(func(Type) = type_only(Type)), Types),
|
|
(
|
|
WithInst = yes(_),
|
|
% MaybePredOrFunc needs to be `no' here because when
|
|
% the item is read from the interface file we won't
|
|
% know whether it is a predicate or a function mode.
|
|
MaybePredOrFunc = no
|
|
;
|
|
WithInst = no,
|
|
MaybePredOrFunc = yes(PredOrFunc)
|
|
),
|
|
PredOrFuncModeItem = pred_or_func_mode(InstVarSet,
|
|
MaybePredOrFunc, SymName, Modes, WithInst,
|
|
MaybeDet, Cond, term__context_init),
|
|
PredOrFuncModeItems = [PredOrFuncModeItem]
|
|
;
|
|
TypesWithoutModes = TypesAndModes,
|
|
PredOrFuncModeItems = []
|
|
),
|
|
varset__init(EmptyInstVarSet),
|
|
PredOrFuncItem = pred_or_func(TVarSet, EmptyInstVarSet,
|
|
ExistQVars, PredOrFunc, SymName,
|
|
TypesWithoutModes, WithType, no, no, Cond, Purity,
|
|
ClassContext, term__context_init),
|
|
Items = [PredOrFuncItem | PredOrFuncModeItems].
|
|
split_class_method_types_and_modes(Method0) = [Method] :-
|
|
% Always strip the context from the item -- this is needed
|
|
% so the items can be easily tested for equality.
|
|
Method0 = pred_or_func_mode(A, B, C, D, E, F, G, _),
|
|
Method = pred_or_func_mode(A, B, C, D, E, F, G, term__context_init).
|
|
|
|
:- pred item_to_item_id(item::in, item_id::out) is semidet.
|
|
|
|
item_to_item_id(Item, ItemId) :-
|
|
item_to_item_id_2(Item, yes(ItemId)).
|
|
|
|
:- pred item_to_item_id_2(item::in, maybe(item_id)::out) is det.
|
|
|
|
item_to_item_id_2(clause(_, _, _, _, _), no).
|
|
item_to_item_id_2(type_defn(_, Name, Params, _, _),
|
|
yes(item_id((type), Name - Arity))) :-
|
|
list__length(Params, Arity).
|
|
item_to_item_id_2(inst_defn(_, Name, Params, _, _),
|
|
yes(item_id((inst), Name - Arity))) :-
|
|
list__length(Params, Arity).
|
|
item_to_item_id_2(mode_defn(_, Name, Params, _, _),
|
|
yes(item_id((mode), Name - Arity))) :-
|
|
list__length(Params, Arity).
|
|
item_to_item_id_2(module_defn(_, _), no).
|
|
item_to_item_id_2(Item, yes(item_id(ItemType, SymName - Arity))) :-
|
|
Item = pred_or_func(_, _, _, PredOrFunc, SymName,
|
|
TypesAndModes, WithType, _, _, _, _, _),
|
|
% For predicates or functions defined using `with_type` annotations
|
|
% the arity here won't be correct, but equiv_type.m will record
|
|
% the dependency on the version number with the `incorrect' arity,
|
|
% so this will work.
|
|
(
|
|
WithType = no,
|
|
adjust_func_arity(PredOrFunc, Arity,
|
|
list__length(TypesAndModes))
|
|
;
|
|
WithType = yes(_),
|
|
Arity = list__length(TypesAndModes)
|
|
),
|
|
ItemType = pred_or_func_to_item_type(PredOrFunc).
|
|
|
|
item_to_item_id_2(Item, ItemId) :-
|
|
Item = pred_or_func_mode(_, MaybePredOrFunc, SymName, Modes,
|
|
_, _, _),
|
|
( MaybePredOrFunc = yes(PredOrFunc) ->
|
|
adjust_func_arity(PredOrFunc, Arity, list__length(Modes)),
|
|
ItemType = pred_or_func_to_item_type(PredOrFunc),
|
|
ItemId = yes(item_id(ItemType, SymName - Arity))
|
|
;
|
|
% We need to handle these separately because a `:- mode'
|
|
% declaration with a `with_inst` annotation could be
|
|
% for a predicate or a funciton.
|
|
ItemId = no
|
|
).
|
|
|
|
% We need to handle these separately because some pragmas
|
|
% may affect a predicate and a function.
|
|
item_to_item_id_2(pragma(_), no).
|
|
item_to_item_id_2(promise(_, _, _, _), no).
|
|
item_to_item_id_2(Item, yes(item_id((typeclass), ClassName - ClassArity))) :-
|
|
Item = typeclass(_, ClassName, ClassVars, _, _),
|
|
list__length(ClassVars, ClassArity).
|
|
|
|
% Instances are handled separately (unlike other items, the module
|
|
% qualifier on an instance declaration is the module containing
|
|
% the class, not the module containing the instance).
|
|
item_to_item_id_2(instance(_, _, _, _, _, _), no).
|
|
item_to_item_id_2(nothing(_), no).
|
|
|
|
:- type maybe_pred_or_func_id ==
|
|
pair(maybe(pred_or_func), sym_name_and_arity).
|
|
|
|
:- pred is_pred_pragma(pragma_type::in,
|
|
maybe(maybe_pred_or_func_id)::out) is det.
|
|
|
|
is_pred_pragma(foreign_decl(_, _), no).
|
|
is_pred_pragma(foreign_import_module(_, _), no).
|
|
is_pred_pragma(foreign_code(_, _), no).
|
|
is_pred_pragma(foreign_proc(_, Name, PredOrFunc, Args, _, _),
|
|
yes(yes(PredOrFunc) - Name / Arity)) :-
|
|
adjust_func_arity(PredOrFunc, Arity, list__length(Args)).
|
|
is_pred_pragma(type_spec(Name, _, Arity, MaybePredOrFunc, _, _, _, _),
|
|
yes(MaybePredOrFunc - Name / Arity)).
|
|
is_pred_pragma(inline(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(no_inline(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(obsolete(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(export(Name, PredOrFunc, Modes, _),
|
|
yes(yes(PredOrFunc) - Name / Arity)) :-
|
|
adjust_func_arity(PredOrFunc, Arity, list__length(Modes)).
|
|
% Pragma import declarations are never used
|
|
% directly by Mercury code.
|
|
is_pred_pragma(import(_, _, _, _, _), no).
|
|
is_pred_pragma(source_file(_), no).
|
|
is_pred_pragma(unused_args(PredOrFunc, Name, Arity, _, _),
|
|
yes(yes(PredOrFunc) - Name / Arity)).
|
|
is_pred_pragma(fact_table(Name, Arity, _), yes(no - Name / Arity)).
|
|
is_pred_pragma(reserve_tag(_TypeName, _TypeArity), no).
|
|
is_pred_pragma(aditi(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(base_relation(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(aditi_index(Name, Arity, _), yes(no - Name / Arity)).
|
|
is_pred_pragma(naive(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(psn(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(aditi_memo(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(aditi_no_memo(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(supp_magic(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(context(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(owner(Name, Arity, _), yes(no - Name / Arity)).
|
|
is_pred_pragma(tabled(_, Name, Arity, MaybePredOrFunc, _),
|
|
yes(MaybePredOrFunc - Name / Arity)).
|
|
is_pred_pragma(promise_pure(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(promise_semipure(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(termination_info(PredOrFunc, Name, Modes, _, _),
|
|
yes(yes(PredOrFunc) - Name / Arity)) :-
|
|
adjust_func_arity(PredOrFunc, Arity, list__length(Modes)).
|
|
is_pred_pragma(terminates(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(does_not_terminate(Name, Arity), yes(no - Name / Arity)).
|
|
is_pred_pragma(check_termination(Name, Arity), yes(no - Name / Arity)).
|
|
|
|
% XXX This is a bit brittle (need to be careful with term__contexts).
|
|
% For example, it won't work for clauses.
|
|
% It will never succeed when it shouldn't, so it will never
|
|
% cause a necessary recompilation to be missed.
|
|
:- pred items_are_unchanged(assoc_list(section, item_and_context)::in,
|
|
assoc_list(section, item_and_context)::in) is semidet.
|
|
|
|
items_are_unchanged([], []).
|
|
items_are_unchanged([Section - (Item1 - _) | Items1],
|
|
[Section - (Item2 - _) | Items2]) :-
|
|
yes = item_is_unchanged(Item1, Item2),
|
|
items_are_unchanged(Items1, Items2).
|
|
|
|
% In most places here, we don't need to compare the varsets.
|
|
% What matters is that the variable numbers in the arguments
|
|
% and body are the same, the names are usually irrelevant.
|
|
%
|
|
% The only places where the names of variables affect the
|
|
% compilation of the program are in explicit type qualifications
|
|
% and `:- pragma type_spec' declarations. Explicit type
|
|
% qualifications do not need to be considered here. This module
|
|
% only deals with items in interface files (we don't yet write type
|
|
% qualifications to `.opt' files). Variables in type qualifications
|
|
% are only matched with the head type variables of the predicate
|
|
% by make_hlds.m. For `:- pragma type_spec' declarations to work
|
|
% we need to consider a predicate or function declaration to be
|
|
% changed if the names of any of the type variables are changed.
|
|
%
|
|
% It's important not to compare the varsets for type and instance
|
|
% declarations because the declarations we get here may be abstract
|
|
% declarations produced from concrete declarations for use in an
|
|
% interface file. The varsets may contain variables from the
|
|
% discarded bodies which will not be present in the items read
|
|
% in from the interface files for comparison.
|
|
%
|
|
% This code assumes that the variables in the head of a
|
|
% type or instance declaration are added to the varset before
|
|
% those from the body, so that the variable numbers in the head of
|
|
% the declaration match those from an abstract declaration read
|
|
% from an interface file.
|
|
:- func item_is_unchanged(item, item) = bool.
|
|
|
|
item_is_unchanged(type_defn(_, Name, Args, Defn, Cond), Item2) =
|
|
( Item2 = type_defn(_, Name, Args, Defn, Cond) -> yes ; no ).
|
|
item_is_unchanged(mode_defn(_VarSet, Name, Args, Defn, Cond), Item2) =
|
|
( Item2 = mode_defn(_, Name, Args, Defn, Cond) -> yes ; no ).
|
|
item_is_unchanged(inst_defn(_VarSet, Name, Args, Defn, Cond), Item2) =
|
|
( Item2 = inst_defn(_, Name, Args, Defn, Cond) -> yes ; no ).
|
|
item_is_unchanged(module_defn(_VarSet, Defn), Item2) =
|
|
( Item2 = module_defn(_, Defn) -> yes ; no ).
|
|
item_is_unchanged(instance(Constraints, Name, Types, Body, _VarSet, Module),
|
|
Item2) =
|
|
( Item2 = instance(Constraints, Name, Types, Body, _, Module) ->
|
|
yes
|
|
;
|
|
no
|
|
).
|
|
|
|
% XXX Need to compare the goals properly in clauses and assertions.
|
|
% That's not necessary at the moment because smart recompilation
|
|
% doesn't work with inter-module optimization yet.
|
|
item_is_unchanged(clause(_VarSet, PorF, SymName, Args, Goal), Item2) =
|
|
( Item2 = clause(_, PorF, SymName, Args, Goal) -> yes ; no ).
|
|
item_is_unchanged(promise(PromiseType, Goal, _, UnivVars), Item2) =
|
|
( Item2 = promise(PromiseType, Goal, _, UnivVars) -> yes ; no ).
|
|
|
|
% We do need to compare the variable names in `:- pragma type_spec'
|
|
% declarations because the names of the variables are used
|
|
% to find the corresponding variables in the predicate or
|
|
% function type declaration.
|
|
item_is_unchanged(pragma(PragmaType1), Item2) = Result :-
|
|
( Item2 = pragma(PragmaType2) ->
|
|
(
|
|
PragmaType1 = type_spec(Name, SpecName, Arity, MaybePredOrFunc,
|
|
MaybeModes, TypeSubst1, TVarSet1, _),
|
|
PragmaType2 = type_spec(Name, SpecName, Arity, MaybePredOrFunc,
|
|
MaybeModes, TypeSubst2, TVarSet2, _)
|
|
->
|
|
assoc_list__keys_and_values(TypeSubst1, TVars1, Types1),
|
|
var_list_to_term_list(TVars1, TVarTypes1),
|
|
assoc_list__keys_and_values(TypeSubst2, TVars2, Types2),
|
|
var_list_to_term_list(TVars2, TVarTypes2),
|
|
(
|
|
type_list_is_unchanged(TVarSet1, TVarTypes1 ++ Types1,
|
|
TVarSet2, TVarTypes2 ++ Types2, _, _, _)
|
|
->
|
|
Result = yes
|
|
;
|
|
Result = no
|
|
)
|
|
;
|
|
Result = ( PragmaType1 = PragmaType2 -> yes ; no )
|
|
)
|
|
;
|
|
Result = no
|
|
).
|
|
item_is_unchanged(nothing(A), Item2) =
|
|
( Item2 = nothing(A) -> yes ; no ).
|
|
|
|
item_is_unchanged(Item1, Item2) = Result :-
|
|
Item1 = pred_or_func(TVarSet1, _, ExistQVars1, PredOrFunc,
|
|
Name, TypesAndModes1, WithType1, _,
|
|
Det1, Cond, Purity, Constraints1),
|
|
(
|
|
Item2 = pred_or_func(TVarSet2, _, ExistQVars2,
|
|
PredOrFunc, Name, TypesAndModes2, WithType2,
|
|
_, Det2, Cond, Purity,
|
|
Constraints2),
|
|
|
|
% For predicates, ignore the determinism -- the modes and
|
|
% determinism should have been split into a separate
|
|
% declaration. This case can only happen if this was
|
|
% not a combined predicate and mode declaration
|
|
% (XXX We should warn about this somewhere).
|
|
% For functions a determinism declaration but no modes
|
|
% implies the default modes. The default modes are
|
|
% added later by make_hlds.m, so they won't have been
|
|
% split into a separate declaration here.
|
|
(
|
|
PredOrFunc = function,
|
|
Det1 = Det2
|
|
;
|
|
PredOrFunc = predicate
|
|
),
|
|
|
|
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1,
|
|
TypesAndModes1, WithType1, Constraints1, TVarSet2,
|
|
ExistQVars2, TypesAndModes2, WithType2, Constraints2)
|
|
->
|
|
Result = yes
|
|
;
|
|
Result = no
|
|
).
|
|
|
|
item_is_unchanged(Item1, Item2) = Result :-
|
|
Item1 = pred_or_func_mode(InstVarSet1, PredOrFunc, Name, Modes1,
|
|
WithInst1, Det, Cond),
|
|
(
|
|
Item2 = pred_or_func_mode(InstVarSet2, PredOrFunc,
|
|
Name, Modes2, WithInst2, Det, Cond),
|
|
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1, WithInst1,
|
|
InstVarSet2, Modes2, WithInst2)
|
|
->
|
|
Result = yes
|
|
;
|
|
Result = no
|
|
).
|
|
|
|
|
|
item_is_unchanged(Item1, Item2) = Result :-
|
|
Item1 = typeclass(Constraints, Name, Vars, Interface1, _VarSet),
|
|
(
|
|
Item2 = typeclass(Constraints, Name, Vars, Interface2, _),
|
|
class_interface_is_unchanged(Interface1, Interface2)
|
|
->
|
|
Result = yes
|
|
;
|
|
Result = no
|
|
).
|
|
|
|
%
|
|
% Apply a substitution to the existq_tvars, types_and_modes, and
|
|
% class_constraints so that the type variables from both declarations
|
|
% being checked are contained in the same tvarset, then check that
|
|
% they are identical.
|
|
%
|
|
% We can't just assume that the varsets will be identical for
|
|
% identical declarations because mercury_to_mercury.m splits
|
|
% combined type and mode declarations into separate declarations.
|
|
% When they are read back in the variable numbers will be different
|
|
% because parser stores the type and inst variables for a combined
|
|
% declaration in a single varset (it doesn't know which are which).
|
|
%
|
|
:- pred pred_or_func_type_is_unchanged(tvarset::in, existq_tvars::in,
|
|
list(type_and_mode)::in, maybe(type)::in, class_constraints::in,
|
|
tvarset::in, existq_tvars::in, list(type_and_mode)::in,
|
|
maybe(type)::in, class_constraints::in) is semidet.
|
|
|
|
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1, TypesAndModes1,
|
|
MaybeWithType1, Constraints1, TVarSet2, ExistQVars2,
|
|
TypesAndModes2, MaybeWithType2, Constraints2) :-
|
|
|
|
GetArgTypes =
|
|
(func(TypeAndMode0) = Type :-
|
|
(
|
|
TypeAndMode0 = type_only(Type)
|
|
;
|
|
% This should have been split out into a
|
|
% separate mode declaration by gather_items.
|
|
TypeAndMode0 = type_and_mode(_, _),
|
|
error(
|
|
"pred_or_func_type_matches: type_and_mode")
|
|
)
|
|
),
|
|
Types1 = list__map(GetArgTypes, TypesAndModes1),
|
|
Types2 = list__map(GetArgTypes, TypesAndModes2),
|
|
(
|
|
MaybeWithType1 = yes(WithType1),
|
|
MaybeWithType2 = yes(WithType2),
|
|
AllTypes1 = [WithType1 | Types1],
|
|
AllTypes2 = [WithType2 | Types2]
|
|
;
|
|
MaybeWithType1 = no,
|
|
MaybeWithType2 = no,
|
|
AllTypes1 = Types1,
|
|
AllTypes2 = Types2
|
|
),
|
|
|
|
type_list_is_unchanged(TVarSet1, AllTypes1, TVarSet2, AllTypes2,
|
|
_TVarSet, RenameSubst, Types2ToTypes1Subst),
|
|
|
|
%
|
|
% Check that the existentially quantified variables are equivalent.
|
|
%
|
|
SubstExistQVars2 =
|
|
term_list_to_var_list(
|
|
term__apply_rec_substitution_to_list(
|
|
apply_substitution_to_list(
|
|
var_list_to_term_list(ExistQVars2),
|
|
RenameSubst),
|
|
Types2ToTypes1Subst)),
|
|
ExistQVars1 = SubstExistQVars2,
|
|
|
|
%
|
|
% Check that the class constraints are identical.
|
|
%
|
|
apply_subst_to_constraints(RenameSubst,
|
|
Constraints2, RenamedConstraints2),
|
|
apply_rec_subst_to_constraints(Types2ToTypes1Subst,
|
|
RenamedConstraints2, SubstConstraints2),
|
|
Constraints1 = SubstConstraints2.
|
|
|
|
:- pred type_list_is_unchanged(tvarset::in, list(type)::in,
|
|
tvarset::in, list(type)::in, tvarset::out,
|
|
tsubst::out, tsubst::out) is semidet.
|
|
|
|
type_list_is_unchanged(TVarSet1, Types1, TVarSet2, Types2,
|
|
TVarSet, RenameSubst, Types2ToTypes1Subst) :-
|
|
varset__merge_subst(TVarSet1, TVarSet2, TVarSet, RenameSubst),
|
|
term__apply_substitution_to_list(Types2, RenameSubst, SubstTypes2),
|
|
|
|
%
|
|
% Check that the types are equivalent
|
|
%
|
|
type_list_subsumes(SubstTypes2, Types1, Types2ToTypes1Subst),
|
|
type_list_subsumes(Types1, SubstTypes2, _),
|
|
|
|
%
|
|
% Check that the corresponding variables have the same names.
|
|
% This is necessary because `:- pragma type_spec' declarations
|
|
% depend on the names of the variables, so for example if two
|
|
% variable names are swapped, the same `:- pragma type_spec'
|
|
% declaration will cause a different specialized version to be
|
|
% created.
|
|
%
|
|
( all [VarInItem1, VarInItem2]
|
|
(
|
|
map__member(Types2ToTypes1Subst, VarInItem2, SubstTerm),
|
|
(
|
|
SubstTerm = term__variable(VarInItem1)
|
|
;
|
|
% The reverse subsumption test above should
|
|
% ensure that the substitutions are all var->var.
|
|
SubstTerm = term__functor(_, _, _),
|
|
error("pred_or_func_type_matches: invalid subst")
|
|
)
|
|
)
|
|
=>
|
|
(
|
|
varset__lookup_name(TVarSet, VarInItem1, VarName1),
|
|
varset__lookup_name(TVarSet, VarInItem2, VarName2),
|
|
(
|
|
VarName1 = VarName2
|
|
;
|
|
%
|
|
% Variables written to interface files are always
|
|
% named, even if the variable in the source code
|
|
% was not, so we can't just use varset__search_name
|
|
% to check whether the variables are named.
|
|
%
|
|
VarIsNotNamed =
|
|
(pred(VarName::in) is semidet :-
|
|
string__append("V_", VarNum, VarName),
|
|
string__to_int(VarNum, _)
|
|
),
|
|
VarIsNotNamed(VarName1),
|
|
VarIsNotNamed(VarName2)
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred pred_or_func_mode_is_unchanged(inst_varset::in, list(mode)::in,
|
|
maybe(inst)::in, inst_varset::in, list(mode)::in,
|
|
maybe(inst)::in) is semidet.
|
|
|
|
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1, MaybeWithInst1,
|
|
InstVarSet2, Modes2, MaybeWithInst2) :-
|
|
varset__coerce(InstVarSet1, VarSet1),
|
|
varset__coerce(InstVarSet2, VarSet2),
|
|
|
|
%
|
|
% Apply the substitution to the modes so that the inst variables
|
|
% from both declarations being checked are contained in the same
|
|
% inst_varset, then check that they are identical.
|
|
%
|
|
varset__merge_subst(VarSet1, VarSet2, _, InstSubst),
|
|
|
|
%
|
|
% Treat modes as types here to use type_list_subsumes, which
|
|
% does just what we want here. (XXX shouldn't type_list_subsumes
|
|
% be in term.m and apply to generic terms anyway?).
|
|
%
|
|
ModeToTerm = (func(Mode) = term__coerce(mode_to_term(Mode))),
|
|
ModeTerms1 = list__map(ModeToTerm, Modes1),
|
|
ModeTerms2 = list__map(ModeToTerm, Modes2),
|
|
(
|
|
MaybeWithInst1 = yes(Inst1),
|
|
MaybeWithInst2 = yes(Inst2),
|
|
WithInstTerm1 = term__coerce(mode_to_term(free -> Inst1)),
|
|
WithInstTerm2 = term__coerce(mode_to_term(free -> Inst2)),
|
|
AllModeTerms1 = [WithInstTerm1 | ModeTerms1],
|
|
AllModeTerms2 = [WithInstTerm2 | ModeTerms2]
|
|
;
|
|
MaybeWithInst1 = no,
|
|
MaybeWithInst2 = no,
|
|
AllModeTerms1 = ModeTerms1,
|
|
AllModeTerms2 = ModeTerms2
|
|
),
|
|
|
|
term__apply_substitution_to_list(AllModeTerms2,
|
|
InstSubst, SubstAllModeTerms2),
|
|
type_list_subsumes(AllModeTerms1, SubstAllModeTerms2, _),
|
|
type_list_subsumes(SubstAllModeTerms2, AllModeTerms1, _).
|
|
|
|
%
|
|
% Combined typeclass method type and mode declarations are split
|
|
% as for ordinary predicate declarations, so the varsets won't
|
|
% necessarily match up if a typeclass declration is read back
|
|
% from an interface file.
|
|
%
|
|
:- pred class_interface_is_unchanged(class_interface::in,
|
|
class_interface::in) is semidet.
|
|
|
|
class_interface_is_unchanged(abstract, abstract).
|
|
class_interface_is_unchanged(concrete(Methods1), concrete(Methods2)) :-
|
|
class_methods_are_unchanged(Methods1, Methods2).
|
|
|
|
:- pred class_methods_are_unchanged(list(class_method)::in,
|
|
list(class_method)::in) is semidet.
|
|
|
|
class_methods_are_unchanged([], []).
|
|
class_methods_are_unchanged([Method1 | Methods1], [Method2 | Methods2]) :-
|
|
(
|
|
Method1 = pred_or_func(TVarSet1, _, ExistQVars1, PredOrFunc,
|
|
Name, TypesAndModes1, WithType1, _,
|
|
Detism, Cond, Purity, Constraints1, _),
|
|
Method2 = pred_or_func(TVarSet2, _, ExistQVars2, PredOrFunc,
|
|
Name, TypesAndModes2, WithType2, _,
|
|
Detism, Cond, Purity, Constraints2, _),
|
|
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1,
|
|
TypesAndModes1, WithType1, Constraints1,
|
|
TVarSet2, ExistQVars2, TypesAndModes2, WithType2,
|
|
Constraints2)
|
|
;
|
|
Method1 = pred_or_func_mode(InstVarSet1, PredOrFunc, Name,
|
|
Modes1, WithInst1, Det, Cond, _),
|
|
Method2 = pred_or_func_mode(InstVarSet2, PredOrFunc, Name,
|
|
Modes2, WithInst2, Det, Cond, _),
|
|
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1, WithInst1,
|
|
InstVarSet2, Modes2, WithInst2)
|
|
),
|
|
class_methods_are_unchanged(Methods1, Methods2).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
recompilation__version__write_version_numbers(
|
|
version_numbers(VersionNumbers, InstanceVersionNumbers)) -->
|
|
{ VersionNumbersList = list__filter_map(
|
|
(func(ItemType) = (ItemType - ItemVersions) is semidet :-
|
|
ItemVersions = extract_ids(VersionNumbers, ItemType),
|
|
\+ map__is_empty(ItemVersions)
|
|
),
|
|
[(type), type_body, (mode), (inst),
|
|
predicate, function, (typeclass)]) },
|
|
io__write_string("{\n\t"),
|
|
io__write_list(VersionNumbersList, ",\n\t",
|
|
(pred((ItemType - ItemVersions)::in, di, uo) is det -->
|
|
{ string_to_item_type(ItemTypeStr, ItemType) },
|
|
io__write_string(ItemTypeStr),
|
|
io__write_string("(\n\t\t"),
|
|
{ map__to_assoc_list(ItemVersions, ItemVersionsList) },
|
|
io__write_list(ItemVersionsList, ",\n\t\t",
|
|
(pred((NameArity - VersionNumber)::in, di, uo) is det -->
|
|
{ NameArity = Name - Arity },
|
|
mercury_output_bracketed_sym_name(unqualified(Name),
|
|
next_to_graphic_token),
|
|
io__write_string("/"),
|
|
io__write_int(Arity),
|
|
io__write_string(" - "),
|
|
write_version_number(VersionNumber)
|
|
)),
|
|
io__write_string("\n\t)")
|
|
)),
|
|
( { map__is_empty(InstanceVersionNumbers) } ->
|
|
[]
|
|
;
|
|
( { VersionNumbersList = [] } ->
|
|
[]
|
|
;
|
|
io__write_string(",\n\t")
|
|
),
|
|
io__write_string("instance("),
|
|
{ map__to_assoc_list(InstanceVersionNumbers, InstanceAL) },
|
|
io__write_list(InstanceAL, ",\n\n\t",
|
|
(pred((ClassNameArity - ClassVersionNumber)::in,
|
|
di, uo) is det -->
|
|
{ ClassNameArity = ClassName - ClassArity },
|
|
mercury_output_bracketed_sym_name(ClassName,
|
|
next_to_graphic_token),
|
|
io__write_string("/"),
|
|
io__write_int(ClassArity),
|
|
io__write_string(" - "),
|
|
write_version_number(ClassVersionNumber)
|
|
)),
|
|
io__write_string(")\n\t")
|
|
),
|
|
io__write_string("\n}").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
version_numbers_version_number = 1.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
parse_version_numbers(VersionNumbersTerm, Result) :-
|
|
(
|
|
VersionNumbersTerm = term__functor(term__atom("{}"),
|
|
VersionNumbersTermList0, _)
|
|
->
|
|
VersionNumbersTermList = VersionNumbersTermList0
|
|
;
|
|
VersionNumbersTermList = [VersionNumbersTerm]
|
|
),
|
|
map_parser(parse_item_type_version_numbers,
|
|
VersionNumbersTermList, Result0),
|
|
(
|
|
Result0 = ok(List),
|
|
VersionNumbers0 = version_numbers(init_item_id_set(map__init),
|
|
map__init),
|
|
VersionNumbers = list__foldl(
|
|
(func(VNResult, version_numbers(VNs0, Instances0)) =
|
|
version_numbers(VNs, Instances) :-
|
|
(
|
|
VNResult = items(ItemType, ItemVNs),
|
|
VNs = update_ids(VNs0, ItemType, ItemVNs),
|
|
Instances = Instances0
|
|
;
|
|
VNResult = instances(Instances),
|
|
VNs = VNs0
|
|
)
|
|
), List, VersionNumbers0),
|
|
Result = ok(VersionNumbers)
|
|
;
|
|
Result0 = error(A, B),
|
|
Result = error(A, B)
|
|
).
|
|
|
|
:- type item_version_numbers_result
|
|
---> items(item_type, version_number_map)
|
|
; instances(instance_version_numbers)
|
|
.
|
|
|
|
:- pred parse_item_type_version_numbers(term::in,
|
|
maybe1(item_version_numbers_result)::out) is det.
|
|
|
|
parse_item_type_version_numbers(Term, Result) :-
|
|
(
|
|
Term = term__functor(term__atom(ItemTypeStr),
|
|
ItemsVNsTerms, _),
|
|
string_to_item_type(ItemTypeStr, ItemType)
|
|
->
|
|
ParseName =
|
|
(pred(NameTerm::in, Name::out) is semidet :-
|
|
NameTerm = term__functor(term__atom(Name), [], _)
|
|
),
|
|
map_parser(parse_item_version_number(ParseName),
|
|
ItemsVNsTerms, Result0),
|
|
(
|
|
Result0 = ok(VNsAL),
|
|
map__from_assoc_list(VNsAL, VNsMap),
|
|
Result = ok(items(ItemType, VNsMap))
|
|
;
|
|
Result0 = error(A, B),
|
|
Result = error(A, B)
|
|
)
|
|
;
|
|
Term = term__functor(term__atom("instance"),
|
|
InstanceVNsTerms, _)
|
|
->
|
|
ParseName =
|
|
(pred(NameTerm::in, Name::out) is semidet :-
|
|
sym_name_and_args(NameTerm, Name, [])
|
|
),
|
|
map_parser(parse_item_version_number(ParseName),
|
|
InstanceVNsTerms, Result1),
|
|
(
|
|
Result1 = ok(VNsAL),
|
|
map__from_assoc_list(VNsAL, VNsMap),
|
|
Result = ok(instances(VNsMap))
|
|
;
|
|
Result1 = error(A, B),
|
|
Result = error(A, B)
|
|
)
|
|
;
|
|
Result = error("invalid item type version numbers",
|
|
Term)
|
|
).
|
|
|
|
:- pred parse_item_version_number(pred(term, T)::(pred(in, out) is semidet),
|
|
term::in, maybe1(pair(pair(T, arity), version_number))::out) is det.
|
|
|
|
parse_item_version_number(ParseName, Term, Result) :-
|
|
(
|
|
Term = term__functor(term__atom("-"),
|
|
[ItemNameArityTerm, VersionNumberTerm], _),
|
|
ItemNameArityTerm = term__functor(term__atom("/"),
|
|
[NameTerm, ArityTerm], _),
|
|
ParseName(NameTerm, Name),
|
|
ArityTerm = term__functor(term__integer(Arity), _, _),
|
|
VersionNumber = term_to_version_number(VersionNumberTerm)
|
|
->
|
|
Result = ok((Name - Arity) - VersionNumber)
|
|
;
|
|
Result = error("error in item version number", Term)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|