Files
mercury/compiler/recompilation_version.m
Peter Ross 77a1261d3b Merge the foreign_type pragma changes from the dotnet branch to the main
Estimated hours taken: 10
Branches: main

Merge the foreign_type pragma changes from the dotnet branch to the main
branch, plus do some more development work to generalise the change.

compiler/prog_data.m:
    Add a type to hold the data from parsing a pragma foreign_type decl.

compiler/prog_io_pragma.m:
    Parse the pragma foreign_type.  This code is currently commented
    out, while we decide on the syntax.

compiler/hlds_data.m:
    Add a new alternative to hlds_type_body where the body of the type
    is a foreign type.

compiler/make_hlds.m:
    Place the foreign_type pragmas into the HLDS.

compiler/foreign.m:
    Implement to_type_string which replaces export__type_to_type_string,
    unlike export__type_to_type_string foreign__to_type_string takes an
    argument specifying which language the representation is meant to be
    in.  to_type_string also needs to take a module_info to handle
    foreign_types correctly.  To avoid the need for the module_info to
    be passed around the MLDS backend we provide a new type
    exported_type which provides enough information for an alternate
    version of to_type_string to be called.

compiler/export.m:
    Delete export__type_to_type_string.

compiler/llds.m:
    Since foreign__to_type_string needs a module_info, we add a new
    field to pragma_c_arg_decl which is the result of calling
    foreign__to_type_string.  This avoids threading the module_info
    around various llds passes.

compiler/mlds.m:
    Record with in the mercury_type the exported_type, this avoids
    passing the module_info around the MLDS backend.
    Also add the foreign_type alternative to mlds__type.
    Update mercury_type_to_mlds_type so that it handles types which are
    foreign types.

compiler/mlds_to_il.m:
    Convert a mlds__foreign_type into an ilds__type.

compiler/ilds.m:
    The CLR spec requires that System.Object and System.String be
    treated specially in the IL assembly so add them as simple types.

compiler/ilasm.m:
    Before outputting a class name into the IL assembly check whether it
    it can be simplified to a builtin type, and if so output that name
    instead as required by the ECMA spec.
    Changes for the addition of string and object as simple types.

doc/reference_manual.texi:
    Document the new pragma, this is currently commented out because it
    refers to syntax that has not yet been finalised.

compiler/fact_table.m:
compiler/llds_out.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_unify_gen.m:
compiler/mlds_to_c.m:
compiler/mlds_to_csharp.m:
compiler/mlds_to_gcc.m:
compiler/mlds_to_java.m:
compiler/mlds_to_mcpp.m:
compiler/pragma_c_gen.m:
compiler/rtti_to_mlds.m:
    Changes to handle using foreign__to_type_string.

compiler/hlds_out.m:
compiler/intermod.m:
compiler/magic_util.m:
compiler/ml_type_gen.m:
compiler/recompilation_usage.m:
compiler/recompilation_version.m:
compiler/term_util.m:
compiler/type_ctor_info.m:
compiler/unify_proc.m:
    Changes to handle the new hlds_type_body.

compiler/mercury_to_mercury.m:
    Output the pragma foreign_type declaration.

compiler/module_qual.m:
    Qualify the pragma foreign_type declarations.

compiler/modules.m:
    Pragma foreign_type is allowed in the interface.
2001-10-24 13:34:41 +00:00

929 lines
32 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2001 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 recompilation, prog_data, prog_io_util, timestamp.
:- 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 mercury_to_mercury, mode_util, prog_io, prog_util, type_util.
:- import_module hlds_out, (inst).
:- 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(Items,
GatheredItems, InstanceItems),
(
MaybeOldItems = yes(OldItems0),
OldItems0 = [VersionNumberItem | OldItems],
VersionNumberItem = module_defn(_,
version_numbers(_, OldVersionNumbers)) - _
->
OldVersionNumbers = version_numbers(OldItemVersionNumbers,
OldInstanceVersionNumbers),
recompilation_version__gather_items(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(item_list::in,
gathered_items::out, instance_item_map::out) is det.
recompilation_version__gather_items(Items, GatheredItems, Instances) :-
list__reverse(Items, RevItems),
Info0 = gathered_item_info(init_item_id_set(map__init),
[], [], map__init),
list__foldl(recompilation_version__gather_items_2, RevItems,
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(
pair(maybe_pred_or_func_id, item_and_context)::in,
gathered_items::in, gathered_items::out) is det.
distribute_pragma_items(ItemId - ItemAndContext,
GatheredItems0, GatheredItems) :-
ItemId = MaybePredOrFunc - SymName / Arity,
ItemAndContext = Item - ItemContext,
AddIfNotExisting = no,
(
MaybePredOrFunc = yes(PredOrFunc),
ItemType = pred_or_func_to_item_type(PredOrFunc),
recompilation_version__add_gathered_item(Item,
item_id(ItemType, SymName - Arity),
ItemContext, AddIfNotExisting,
GatheredItems0, GatheredItems2)
;
MaybePredOrFunc = no,
recompilation_version__add_gathered_item(Item,
item_id(predicate, SymName - Arity),
ItemContext, AddIfNotExisting,
GatheredItems0, GatheredItems1),
adjust_func_arity(function, Arity, FuncArity),
recompilation_version__add_gathered_item(Item,
item_id(function, SymName - FuncArity),
ItemContext, 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, _, _, _, _, _),
( MaybePredOrFunc = yes(MethodPredOrFunc)
; MaybePredOrFunc = no
),
adjust_func_arity(MethodPredOrFunc,
Arity, list__length(TypesAndModes))
->
% XXX O(N^2), but shouldn't happen too often.
ClassItems = ClassItems0 ++ [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 :: assoc_list(maybe_pred_or_func_id,
item_and_context),
other_items :: item_list,
instances :: instance_item_map
).
:- type instance_item_map == map(item_name, item_list).
% The constructors set should always be empty.
:- type gathered_items == item_id_set(map(pair(string, arity), item_list)).
:- pred recompilation_version__gather_items_2(item_and_context::in,
gathered_item_info::in, gathered_item_info::out) is det.
recompilation_version__gather_items_2(ItemAndContext) -->
{ ItemAndContext = Item - ItemContext },
(
{ 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(_, _) },
{ NameItem = type_defn(VarSet, Name, Args,
abstract_type, Cond) },
{ BodyItem = Item }
;
{ Body = eqv_type(_) },
% When we use an equivalence type we
% always use the body.
{ NameItem = Item },
{ BodyItem = Item }
;
{ Body = uu_type(_) },
{ error(
"recompilation_version__gather_items_2: uu_type") }
),
{ TypeId = Name - list__length(Args) },
GatheredItems0 =^ gathered_items,
{ recompilation_version__add_gathered_item(NameItem,
item_id((type), TypeId), ItemContext,
yes, GatheredItems0, GatheredItems1) },
{ recompilation_version__add_gathered_item(BodyItem,
item_id(type_body, TypeId), ItemContext,
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,
[Item - ItemContext | InstanceItems], Instances) },
^ instances := Instances
;
{ item_to_item_id(Item, ItemId) }
->
GatheredItems0 =^ gathered_items,
{ recompilation_version__add_gathered_item(Item, ItemId,
ItemContext, yes, GatheredItems0, GatheredItems) },
^ gathered_items := GatheredItems
;
{ Item = pragma(PragmaType) },
{ is_pred_pragma(PragmaType, yes(PredOrFuncId)) }
->
PragmaItems =^ pragma_items,
^ pragma_items := [PredOrFuncId - ItemAndContext | PragmaItems]
;
OtherItems =^ other_items,
^ other_items := [ItemAndContext | OtherItems]
).
:- pred recompilation_version__add_gathered_item(item::in, item_id::in,
prog_context::in, bool::in, gathered_items::in,
gathered_items::out) is det.
recompilation_version__add_gathered_item(Item, ItemId, ItemContext,
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, MatchingItems,
GatheredItems0, GatheredItems)
).
:- pred recompilation_version__add_gathered_item_2(item::in, item_type::in,
pair(string, arity)::in, prog_context::in, item_list::in,
gathered_items::in, gathered_items::out) is det.
recompilation_version__add_gathered_item_2(Item, ItemType, NameArity,
ItemContext, 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, Det,
Cond, Purity, ClassContext),
split_types_and_modes(TypesAndModes, Types, MaybeModes),
MaybeModes = yes(Modes)
->
TypesWithoutModes = list__map(
(func(Type) = type_only(Type)), Types),
varset__init(EmptyInstVarSet),
PredOrFuncItem = pred_or_func(TVarSet, EmptyInstVarSet,
ExistQVars, PredOrFunc, PredName, TypesWithoutModes,
no, Cond, Purity, ClassContext),
PredOrFuncModeItem = pred_or_func_mode(InstVarSet,
PredOrFunc, PredName, Modes, Det, Cond),
MatchingItems =
[PredOrFuncItem - ItemContext,
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 = [TypeclassItem - ItemContext | MatchingItems0]
;
MatchingItems = [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, MaybeDet,
Cond, Purity, ClassContext, _),
(
split_types_and_modes(TypesAndModes, Types, MaybeModes),
MaybeModes = yes(Modes)
->
TypesWithoutModes = list__map(
(func(Type) = type_only(Type)), Types),
PredOrFuncModeItem = pred_or_func_mode(InstVarSet, PredOrFunc,
SymName, Modes, MaybeDet, Cond, term__context_init),
PredOrFuncModeItems = [PredOrFuncModeItem]
;
TypesWithoutModes = TypesAndModes,
PredOrFuncModeItems = []
),
varset__init(EmptyInstVarSet),
PredOrFuncItem = pred_or_func(TVarSet, EmptyInstVarSet,
ExistQVars, PredOrFunc, SymName,
TypesWithoutModes, 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, _),
Method = pred_or_func_mode(A, B, C, D, E, F, 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, _, _, _, _),
adjust_func_arity(PredOrFunc, Arity, list__length(TypesAndModes)),
ItemType = pred_or_func_to_item_type(PredOrFunc).
item_to_item_id_2(Item, yes(item_id(ItemType, SymName - Arity))) :-
Item = pred_or_func_mode(_, PredOrFunc, SymName, Modes, _, _),
adjust_func_arity(PredOrFunc, Arity, list__length(Modes)),
ItemType = pred_or_func_to_item_type(PredOrFunc).
% 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(assertion(_, _), 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_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(foreign_type(_, _, _, _), no).
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(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(item_list::in, item_list::in) is semidet.
items_are_unchanged([], []).
items_are_unchanged([Item1 - _ | Items1], [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(_VarSet, 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(assertion(Goal, _VarSet), Item2) =
( Item2 = assertion(Goal, _) -> yes ; no ).
% We do need to compare the varset 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(PragmaType), Item2) =
( Item2 = pragma(PragmaType) -> yes ; 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, Detism, Cond, Purity, Constraints1),
(
Item2 = pred_or_func(TVarSet2, _, ExistQVars2,
PredOrFunc, Name, TypesAndModes2, Detism, Cond, Purity,
Constraints2),
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1,
TypesAndModes1, Constraints1, TVarSet2,
ExistQVars2, TypesAndModes2, Constraints2)
->
Result = yes
;
Result = no
).
item_is_unchanged(Item1, Item2) = Result :-
Item1 = pred_or_func_mode(InstVarSet1, PredOrFunc, Name, Modes1,
Det, Cond),
(
Item2 = pred_or_func_mode(InstVarSet2, PredOrFunc,
Name, Modes2, Det, Cond),
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1,
InstVarSet2, Modes2)
->
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, class_constraints::in,
tvarset::in, existq_tvars::in, list(type_and_mode)::in,
class_constraints::in) is semidet.
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1, TypesAndModes1,
Constraints1, TVarSet2, ExistQVars2,
TypesAndModes2, Constraints2) :-
varset__merge_subst(TVarSet1, TVarSet2, TVarSet, Subst),
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),
term__apply_substitution_to_list(Types2, Subst, 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, VarName),
varset__lookup_name(TVarSet, VarInItem2, VarName)
)
),
%
% 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),
Subst),
Types2ToTypes1Subst)),
ExistQVars1 = SubstExistQVars2,
%
% Check that the class constraints are identical.
%
apply_subst_to_constraints(Subst, Constraints2, RenamedConstraints2),
apply_rec_subst_to_constraints(Types2ToTypes1Subst,
RenamedConstraints2, SubstConstraints2),
Constraints1 = SubstConstraints2.
:- pred pred_or_func_mode_is_unchanged(inst_varset::in, list(mode)::in,
inst_varset::in, list(mode)::in) is semidet.
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1, InstVarSet2, Modes2) :-
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),
term__apply_substitution_to_list(ModeTerms2,
InstSubst, SubstModeTerms2),
type_list_subsumes(ModeTerms1, SubstModeTerms2, _),
type_list_subsumes(SubstModeTerms2, ModeTerms1, _).
%
% 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, Detism, Cond, Purity,
Constraints1, _),
Method2 = pred_or_func(TVarSet2, _, ExistQVars2, PredOrFunc,
Name, TypesAndModes2, Detism, Cond, Purity,
Constraints2, _),
pred_or_func_type_is_unchanged(TVarSet1, ExistQVars1,
TypesAndModes1, Constraints1, TVarSet2, ExistQVars2,
TypesAndModes2, Constraints2)
;
Method1 = pred_or_func_mode(InstVarSet1, PredOrFunc, Name,
Modes1, Det, Cond, _),
Method2 = pred_or_func_mode(InstVarSet2, PredOrFunc, Name,
Modes2, Det, Cond, _),
pred_or_func_mode_is_unchanged(InstVarSet1, Modes1,
InstVarSet2, Modes2)
),
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)
).
%-----------------------------------------------------------------------------%