mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 05:12:33 +00:00
Estimated hours taken: 60 User-guided type specialization. compiler/prog_data.m: compiler/prog_io_pragma.m: compiler/modules.m: compiler/module_qual.m: compiler/mercury_to_mercury.m: Handle `:- pragma type_spec'. compiler/prog_io_pragma.m: Factor out some common code to parse predicate names with arguments. compiler/hlds_module.m: Added a field to the module_sub_info to hold information about user-requested type specializations, filled in by make_hlds.m and not used by anything after higher_order.m. compiler/make_hlds.m: For each `:- pragma type_spec' declaration, introduce a new predicate which just calls the predicate to be specialized with the specified argument types. This forces higher_order.m to produce the specialized versions. compiler/higher_order.m: Process the user-requested type specializations first to ensure that they get the correct names. Allow partial matches against user-specified versions, e.g. map__lookup(map(int, list(int)), int, list(int)) matches map__lookup(map(int, V), int, V). Perform specialization where a typeclass constraint matches a known instance, but the construction of the typeclass_info is done in the calling module. Give slightly more informative progress messages. compiler/dead_proc_elim.m: Remove specializations for dead procedures. compiler/prog_io_util.m: Change the definition of the `maybe1' and `maybe_functor' types to avoid the need for copying to convert between `maybe1' and `maybe1(generic)'. Changed the interface of `make_pred_name_with_context' to allow creation of predicate names for type specializations which describe the type substitution. compiler/make_hlds.m: compiler/prog_io_pragma.m: Make the specification of pragma declarations in error messages consistent. (There are probably some more to be fixed elsewhere for termination and tabling). compiler/intermod.m: Write type specialization pragmas for predicates declared in `.opt' files. compiler/mercury_to_mercury.m: Export `mercury_output_item' for use by intermod.m. compiler/options.m: Add an option `--user-guided-type-specialization' enabled with `-O2' or higher. compiler/handle_options.m: `--type-specialization' implies `--user-guided-type-specialization'. compiler/hlds_goal.m: Add predicates to construct constants. These are duplicated in several other places, I'll fix that as a separate change. compiler/type_util.m: Added functions `int_type/0', `string_type/0', `float_type/0' and `char_type/0' which return the builtin types. These are duplicated in several other places, I'll fix that as a separate change. library/private_builtin.m: Added `instance_constraint_from_typeclass_info/3' to extract the typeclass_infos for a constraint on an instance declaration. This is useful for specializing class method calls. Added `thread_safe' to various `:- pragma c_code's. Added `:- pragma inline' declarations for `builtin_compare_*', which are important for user-guided type specialization. (`builtin_unify_*' are simple enough to go in the `.opt' files automatically). compiler/polymorphism.m: `instance_constraint_from_typeclass_info/3' does not need type_infos. Add `instance_constraint_from_typeclass_info/3' to the list of `typeclass_info_manipulator's which higher_order.m can interpret. NEWS: doc/reference_manual.texi: doc/user_guide.texi Document the new pragma and option. tests/invalid/Mmakefile: tests/invalid/type_spec.m: tests/invalid/type_spec.err_exp: Test error reporting for invalid type specializations. tests/hard_coded/Mmakefile: tests/invalid/type_spec.m: tests/invalid/type_spec.exp: Test type specialization.
963 lines
32 KiB
Mathematica
963 lines
32 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1999 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: type_util.m.
|
|
% Main author: fjh.
|
|
|
|
% This file provides some utility predicates which operate on types.
|
|
% It is used by various stages of the compilation after type-checking,
|
|
% include the mode checker and the code generator.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module type_util.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_module, hlds_pred, hlds_data, prog_data.
|
|
:- import_module term.
|
|
:- import_module list, map.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Succeed iff type is an "atomic" type - one which can be
|
|
% unified using a simple_test rather than a complicated_unify.
|
|
|
|
:- pred type_is_atomic(type, module_info).
|
|
:- mode type_is_atomic(in, in) is semidet.
|
|
|
|
% type_is_higher_order(Type, PredOrFunc, ArgTypes) succeeds iff
|
|
% Type is a higher-order predicate or function type with the specified
|
|
% argument types (for functions, the return type is appended to the
|
|
% end of the argument types).
|
|
|
|
:- pred type_is_higher_order(type, pred_or_func, list(type)).
|
|
:- mode type_is_higher_order(in, out, out) is semidet.
|
|
|
|
% type_id_is_higher_order(TypeId, PredOrFunc) succeeds iff
|
|
% TypeId is a higher-order predicate or function type.
|
|
|
|
:- pred type_id_is_higher_order(type_id, pred_or_func).
|
|
:- mode type_id_is_higher_order(in, out) is semidet.
|
|
|
|
:- pred type_is_aditi_state(type).
|
|
:- mode type_is_aditi_state(in) is semidet.
|
|
|
|
% A test for types that are defined by hand (not including
|
|
% the builtin types). Don't generate type_ctor_*
|
|
% for these types.
|
|
|
|
:- pred type_id_is_hand_defined(type_id).
|
|
:- mode type_id_is_hand_defined(in) is semidet.
|
|
|
|
% Given a type, determine what sort of type it is.
|
|
|
|
:- pred classify_type(type, module_info, builtin_type).
|
|
:- mode classify_type(in, in, out) is det.
|
|
|
|
:- type builtin_type ---> int_type
|
|
; char_type
|
|
; str_type
|
|
; float_type
|
|
; pred_type
|
|
; enum_type
|
|
; polymorphic_type
|
|
; user_type.
|
|
|
|
% Given a non-variable type, return its type-id and argument types.
|
|
|
|
:- pred type_to_type_id(type, type_id, list(type)).
|
|
:- mode type_to_type_id(in, out, out) is semidet.
|
|
|
|
% Given a variable type, return its type variable.
|
|
|
|
:- pred type_util__var(type, tvar).
|
|
:- mode type_util__var(in, out) is semidet.
|
|
:- mode type_util__var(out, in) is det.
|
|
|
|
% Given a type_id, a list of argument types and maybe a context,
|
|
% construct a type.
|
|
|
|
:- pred construct_type(type_id, list(type), (type)).
|
|
:- mode construct_type(in, in, out) is det.
|
|
|
|
:- pred construct_type(type_id, list(type), prog_context, (type)).
|
|
:- mode construct_type(in, in, in, out) is det.
|
|
|
|
% Construct builtin types.
|
|
:- func int_type = (type).
|
|
:- func string_type = (type).
|
|
:- func float_type = (type).
|
|
:- func char_type = (type).
|
|
|
|
% Given a constant and an arity, return a type_id.
|
|
% Fails if the constant is not an atom.
|
|
|
|
:- pred make_type_id(const, int, type_id).
|
|
:- mode make_type_id(in, in, out) is semidet.
|
|
|
|
% Given a type_id, look up its module/name/arity
|
|
|
|
:- pred type_util__type_id_module(module_info, type_id, module_name).
|
|
:- mode type_util__type_id_module(in, in, out) is det.
|
|
|
|
:- pred type_util__type_id_name(module_info, type_id, string).
|
|
:- mode type_util__type_id_name(in, in, out) is det.
|
|
|
|
:- pred type_util__type_id_arity(module_info, type_id, arity).
|
|
:- mode type_util__type_id_arity(in, in, out) is det.
|
|
|
|
% If the type is a du type, return the list of its constructors.
|
|
|
|
:- pred type_constructors(type, module_info, list(constructor)).
|
|
:- mode type_constructors(in, in, out) is semidet.
|
|
|
|
% Work out the types of the arguments of a functor.
|
|
:- pred type_util__get_cons_id_arg_types(module_info::in, (type)::in,
|
|
cons_id::in, list(type)::out) is det.
|
|
|
|
% Given a list of constructors for a type,
|
|
% check whether that type is a no_tag type
|
|
% (i.e. one with only one constructor, and
|
|
% whose one constructor has only one argument,
|
|
% and which is not private_builtin:type_info/1),
|
|
% and if so, return its constructor symbol and argument type.
|
|
|
|
:- pred type_is_no_tag_type(list(constructor), sym_name, type).
|
|
:- mode type_is_no_tag_type(in, out, out) is semidet.
|
|
|
|
% Unify (with occurs check) two types with respect to a type
|
|
% substitution and update the type bindings.
|
|
% The third argument is a list of type variables which cannot
|
|
% be bound (i.e. head type variables).
|
|
|
|
:- pred type_unify(type, type, list(tvar), tsubst, tsubst).
|
|
:- mode type_unify(in, in, in, in, out) is semidet.
|
|
|
|
:- pred type_unify_list(list(type), list(type), list(tvar), tsubst, tsubst).
|
|
:- mode type_unify_list(in, in, in, in, out) is semidet.
|
|
|
|
% Return a list of the type variables of a type.
|
|
|
|
:- pred type_util__vars(type, list(tvar)).
|
|
:- mode type_util__vars(in, out) is det.
|
|
|
|
% type_list_subsumes(TypesA, TypesB, Subst) succeeds iff the list
|
|
% TypesA subsumes (is more general than) TypesB, producing a
|
|
% type substitution which when applied to TypesA will give TypesB.
|
|
|
|
:- pred type_list_subsumes(list(type), list(type), tsubst).
|
|
:- mode type_list_subsumes(in, in, out) is semidet.
|
|
|
|
% apply a type substitution (i.e. map from tvar -> type)
|
|
% to all the types in a variable typing (i.e. map from var -> type).
|
|
|
|
:- pred apply_substitution_to_type_map(map(prog_var, type), tsubst,
|
|
map(prog_var, type)).
|
|
:- mode apply_substitution_to_type_map(in, in, out) is det.
|
|
|
|
% same thing as above, except for a recursive substitution
|
|
% (i.e. we keep applying the substitution recursively until
|
|
% there are no more changes).
|
|
|
|
:- pred apply_rec_substitution_to_type_map(map(prog_var, type), tsubst,
|
|
map(prog_var, type)).
|
|
:- mode apply_rec_substitution_to_type_map(in, in, out) is det.
|
|
|
|
% Update a map from tvar to type_info_locn, using the type renaming
|
|
% and substitution to rename tvars and a variable substitution to
|
|
% rename vars. The type renaming is applied before the type
|
|
% substitution.
|
|
%
|
|
% If tvar maps to a another type variable, we keep the new
|
|
% variable, if it maps to a type, we remove it from the map.
|
|
|
|
:- pred apply_substitutions_to_var_map(map(tvar, type_info_locn), tsubst,
|
|
map(tvar, type), map(prog_var, prog_var), map(tvar, type_info_locn)).
|
|
:- mode apply_substitutions_to_var_map(in, in, in, in, out) is det.
|
|
|
|
% Update a map from class_constraint to var, using the type renaming
|
|
% and substitution to rename tvars and a variable substition to
|
|
% rename vars. The type renaming is applied before the type
|
|
% substitution.
|
|
|
|
:- pred apply_substitutions_to_typeclass_var_map(
|
|
map(class_constraint, prog_var), tsubst, map(tvar, type),
|
|
map(prog_var, prog_var), map(class_constraint, prog_var)).
|
|
:- mode apply_substitutions_to_typeclass_var_map(in, in, in, in, out) is det.
|
|
|
|
:- pred apply_rec_subst_to_constraints(tsubst, class_constraints,
|
|
class_constraints).
|
|
:- mode apply_rec_subst_to_constraints(in, in, out) is det.
|
|
|
|
:- pred apply_rec_subst_to_constraint_list(tsubst,
|
|
list(class_constraint), list(class_constraint)).
|
|
:- mode apply_rec_subst_to_constraint_list(in, in, out) is det.
|
|
|
|
:- pred apply_rec_subst_to_constraint(tsubst, class_constraint,
|
|
class_constraint).
|
|
:- mode apply_rec_subst_to_constraint(in, in, out) is det.
|
|
|
|
:- pred apply_subst_to_constraints(tsubst, class_constraints,
|
|
class_constraints).
|
|
:- mode apply_subst_to_constraints(in, in, out) is det.
|
|
|
|
:- pred apply_subst_to_constraint_list(tsubst, list(class_constraint),
|
|
list(class_constraint)).
|
|
:- mode apply_subst_to_constraint_list(in, in, out) is det.
|
|
|
|
:- pred apply_subst_to_constraint(tsubst, class_constraint,
|
|
class_constraint).
|
|
:- mode apply_subst_to_constraint(in, in, out) is det.
|
|
|
|
:- pred apply_subst_to_constraint_proofs(tsubst,
|
|
map(class_constraint, constraint_proof),
|
|
map(class_constraint, constraint_proof)).
|
|
:- mode apply_subst_to_constraint_proofs(in, in, out) is det.
|
|
|
|
:- pred apply_rec_subst_to_constraint_proofs(tsubst,
|
|
map(class_constraint, constraint_proof),
|
|
map(class_constraint, constraint_proof)).
|
|
:- mode apply_rec_subst_to_constraint_proofs(in, in, out) is det.
|
|
|
|
:- pred apply_variable_renaming_to_constraints(map(tvar, tvar),
|
|
class_constraints, class_constraints).
|
|
:- mode apply_variable_renaming_to_constraints(in, in, out) is det.
|
|
|
|
:- pred apply_variable_renaming_to_constraint_list(map(tvar, tvar),
|
|
list(class_constraint), list(class_constraint)).
|
|
:- mode apply_variable_renaming_to_constraint_list(in, in, out) is det.
|
|
|
|
:- pred apply_variable_renaming_to_constraint(map(tvar, tvar),
|
|
class_constraint, class_constraint).
|
|
:- mode apply_variable_renaming_to_constraint(in, in, out) is det.
|
|
|
|
% Apply a renaming (partial map) to a list.
|
|
% Useful for applying a variable renaming to a list of variables.
|
|
:- pred apply_partial_map_to_list(list(T), map(T, T), list(T)).
|
|
:- mode apply_partial_map_to_list(in, in, out) is det.
|
|
|
|
% strip out the prog_context fields, replacing them with empty
|
|
% prog_context (as obtained by term__context_init/1)
|
|
% in a type or list of types
|
|
:- pred strip_prog_contexts(list(term(T))::in, list(term(T))::out) is det.
|
|
:- pred strip_prog_context(term(T)::in, term(T)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module bool, require, std_util.
|
|
:- import_module prog_io, prog_io_goal, prog_util.
|
|
|
|
type_util__type_id_module(_ModuleInfo, TypeName - _Arity, ModuleName) :-
|
|
sym_name_get_module_name(TypeName, unqualified(""), ModuleName).
|
|
|
|
type_util__type_id_name(_ModuleInfo, Name0 - _Arity, Name) :-
|
|
unqualify_name(Name0, Name).
|
|
|
|
type_util__type_id_arity(_ModuleInfo, _Name - Arity, Arity).
|
|
|
|
type_is_atomic(Type, ModuleInfo) :-
|
|
classify_type(Type, ModuleInfo, BuiltinType),
|
|
BuiltinType \= polymorphic_type,
|
|
BuiltinType \= pred_type,
|
|
BuiltinType \= user_type.
|
|
|
|
type_util__var(term__variable(Var), Var).
|
|
|
|
type_id_is_hand_defined(qualified(PrivateBuiltin, "type_info") - 1) :-
|
|
mercury_private_builtin_module(PrivateBuiltin).
|
|
type_id_is_hand_defined(qualified(PrivateBuiltin, "type_ctor_info") - 1) :-
|
|
mercury_private_builtin_module(PrivateBuiltin).
|
|
type_id_is_hand_defined(qualified(PrivateBuiltin, "typeclass_info") - 1) :-
|
|
mercury_private_builtin_module(PrivateBuiltin).
|
|
type_id_is_hand_defined(qualified(PrivateBuiltin, "base_typeclass_info") - 1) :-
|
|
mercury_private_builtin_module(PrivateBuiltin).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a type, determine what sort of type it is.
|
|
|
|
classify_type(VarType, ModuleInfo, Type) :-
|
|
( type_to_type_id(VarType, TypeId, _) ->
|
|
( TypeId = unqualified("character") - 0 ->
|
|
Type = char_type
|
|
; TypeId = unqualified("int") - 0 ->
|
|
Type = int_type
|
|
; TypeId = unqualified("float") - 0 ->
|
|
Type = float_type
|
|
; TypeId = unqualified("string") - 0 ->
|
|
Type = str_type
|
|
; type_id_is_higher_order(TypeId, _) ->
|
|
Type = pred_type
|
|
; type_id_is_enumeration(TypeId, ModuleInfo) ->
|
|
Type = enum_type
|
|
;
|
|
Type = user_type
|
|
)
|
|
;
|
|
Type = polymorphic_type
|
|
).
|
|
|
|
type_is_higher_order(Type, PredOrFunc, PredArgTypes) :-
|
|
(
|
|
Type = term__functor(term__atom("pred"),
|
|
PredArgTypes, _),
|
|
PredOrFunc = predicate
|
|
;
|
|
Type = term__functor(term__atom("="),
|
|
[term__functor(term__atom("func"),
|
|
FuncArgTypes, _),
|
|
FuncRetType], _),
|
|
list__append(FuncArgTypes, [FuncRetType], PredArgTypes),
|
|
PredOrFunc = function
|
|
).
|
|
|
|
type_id_is_higher_order(SymName - Arity, PredOrFunc) :-
|
|
unqualify_name(SymName, TypeName),
|
|
(
|
|
TypeName = "pred",
|
|
PredOrFunc = predicate
|
|
;
|
|
TypeName = "=",
|
|
Arity = 2,
|
|
PredOrFunc = function
|
|
).
|
|
|
|
type_is_aditi_state(Type) :-
|
|
type_to_type_id(Type,
|
|
qualified(unqualified("aditi"), "state") - 0, []).
|
|
|
|
:- pred type_id_is_enumeration(type_id, module_info).
|
|
:- mode type_id_is_enumeration(in, in) is semidet.
|
|
|
|
type_id_is_enumeration(TypeId, ModuleInfo) :-
|
|
module_info_types(ModuleInfo, TypeDefnTable),
|
|
map__search(TypeDefnTable, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
|
|
TypeBody = du_type(_, _, IsEnum, _),
|
|
IsEnum = yes.
|
|
|
|
type_to_type_id(Type, SymName - Arity, Args) :-
|
|
sym_name_and_args(Type, SymName, Args1),
|
|
|
|
% `private_builtin:constraint' is introduced by polymorphism, and
|
|
% should only appear as the argument of a `typeclass:info/1' type.
|
|
% It behaves sort of like a type variable, so according to the
|
|
% specification of `type_to_type_id', it should cause failure.
|
|
% There isn't a definition in the type table.
|
|
mercury_private_builtin_module(PrivateBuiltin),
|
|
SymName \= qualified(PrivateBuiltin, "constraint"),
|
|
|
|
% higher order types may have representations where
|
|
% their arguments don't directly correspond to the
|
|
% arguments of the term.
|
|
(
|
|
type_is_higher_order(Type, _, PredArgTypes)
|
|
->
|
|
Args = PredArgTypes,
|
|
list__length(Args1, Arity) % functions have arity 2,
|
|
% (they are =/2)
|
|
;
|
|
Args = Args1,
|
|
list__length(Args, Arity)
|
|
).
|
|
|
|
construct_type(TypeId, Args, Type) :-
|
|
term__context_init(Context),
|
|
construct_type(TypeId, Args, Context, Type).
|
|
|
|
construct_type(TypeId, Args, Context, Type) :-
|
|
(
|
|
type_id_is_higher_order(TypeId, PredOrFunc)
|
|
->
|
|
(
|
|
PredOrFunc = predicate,
|
|
NewArgs = Args
|
|
;
|
|
PredOrFunc = function,
|
|
pred_args_to_func_args(Args, FuncArgTypes, FuncRetType),
|
|
NewArgs = [term__functor(term__atom("func"),
|
|
FuncArgTypes, Context),
|
|
FuncRetType]
|
|
)
|
|
;
|
|
NewArgs = Args
|
|
),
|
|
TypeId = SymName - _,
|
|
construct_qualified_term(SymName, NewArgs, Context, Type).
|
|
|
|
int_type = Type :- construct_type(unqualified("int") - 0, [], Type).
|
|
string_type = Type :- construct_type(unqualified("string") - 0, [], Type).
|
|
float_type = Type :- construct_type(unqualified("float") - 0, [], Type).
|
|
char_type = Type :- construct_type(unqualified("character") - 0, [], Type).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a constant and an arity, return a type_id.
|
|
% This really ought to take a name and an arity -
|
|
% use of integers/floats/strings as type names should
|
|
% be rejected by the parser in prog_io.m, not in module_qual.m.
|
|
|
|
make_type_id(term__atom(Name), Arity, unqualified(Name) - Arity).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% If the type is a du type, return the list of its constructors.
|
|
|
|
type_constructors(Type, ModuleInfo, Constructors) :-
|
|
type_to_type_id(Type, TypeId, TypeArgs),
|
|
module_info_types(ModuleInfo, TypeTable),
|
|
map__search(TypeTable, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_tparams(TypeDefn, TypeParams),
|
|
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
|
|
TypeBody = du_type(Constructors0, _, _, _),
|
|
substitute_type_args(TypeParams, TypeArgs, Constructors0,
|
|
Constructors).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
type_util__get_cons_id_arg_types(ModuleInfo, VarType, ConsId, ArgTypes) :-
|
|
(
|
|
type_to_type_id(VarType, TypeId, TypeArgs),
|
|
module_info_ctors(ModuleInfo, Ctors),
|
|
% will fail for builtin cons_ids.
|
|
map__search(Ctors, ConsId, ConsDefns),
|
|
CorrectCons = lambda([ConsDefn::in] is semidet, (
|
|
ConsDefn = hlds_cons_defn(_, _, _, TypeId, _)
|
|
)),
|
|
list__filter(CorrectCons, ConsDefns,
|
|
[hlds_cons_defn(_, _, ArgTypes0, _, _)]),
|
|
ArgTypes0 \= []
|
|
->
|
|
module_info_types(ModuleInfo, Types),
|
|
map__lookup(Types, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_tparams(TypeDefn, TypeDefnParams),
|
|
term__term_list_to_var_list(TypeDefnParams, TypeDefnVars),
|
|
term__substitute_corresponding_list(TypeDefnVars, TypeArgs,
|
|
ArgTypes0, ArgTypes)
|
|
;
|
|
ArgTypes = []
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The checks for type_info and type_ctor_info
|
|
% are needed because those types lie about their
|
|
% arity; it might be cleaner to change that in
|
|
% private_builtin.m, but that would cause some
|
|
% bootstrapping difficulties.
|
|
% It might be slightly better to check for private_builtin:type_info
|
|
% etc. rather than just checking the unqualified type name,
|
|
% but I found it difficult to verify that the constructors
|
|
% would always be fully module-qualified at points where
|
|
% type_is_no_tag_type/3 is called.
|
|
|
|
type_is_no_tag_type(Ctors, Ctor, Type) :-
|
|
Ctors = [SingleCtor],
|
|
SingleCtor = ctor(ExistQVars, _Constraints, Ctor, [_FieldName - Type]),
|
|
ExistQVars = [],
|
|
unqualify_name(Ctor, Name),
|
|
Name \= "type_info",
|
|
Name \= "type_ctor_info",
|
|
Name \= "typeclass_info",
|
|
Name \= "base_typeclass_info".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Substitute the actual values of the type parameters
|
|
% in list of constructors, for a particular instance of
|
|
% a polymorphic type.
|
|
|
|
:- pred substitute_type_args(list(type_param), list(type),
|
|
list(constructor), list(constructor)).
|
|
:- mode substitute_type_args(in, in, in, out) is det.
|
|
|
|
substitute_type_args(TypeParams0, TypeArgs, Constructors0, Constructors) :-
|
|
( TypeParams0 = [] ->
|
|
Constructors = Constructors0
|
|
;
|
|
term__term_list_to_var_list(TypeParams0, TypeParams),
|
|
map__from_corresponding_lists(TypeParams, TypeArgs, Subst),
|
|
substitute_type_args_2(Constructors0, Subst, Constructors)
|
|
).
|
|
|
|
:- pred substitute_type_args_2(list(constructor), tsubst,
|
|
list(constructor)).
|
|
:- mode substitute_type_args_2(in, in, out) is det.
|
|
|
|
substitute_type_args_2([], _, []).
|
|
substitute_type_args_2([Ctor0| Ctors0], Subst, [Ctor | Ctors]) :-
|
|
% Note: prog_io.m ensures that the existentially quantified
|
|
% variables, if any, are distinct from the parameters,
|
|
% and that the (existential) constraints can only contain
|
|
% existentially quantified variables, so there's
|
|
% no need to worry about applying the substitution to
|
|
% ExistQVars or Constraints
|
|
Ctor0 = ctor(ExistQVars, Constraints, Name, Args0),
|
|
Ctor = ctor(ExistQVars, Constraints, Name, Args),
|
|
substitute_type_args_3(Args0, Subst, Args),
|
|
substitute_type_args_2(Ctors0, Subst, Ctors).
|
|
|
|
:- pred substitute_type_args_3(list(constructor_arg), tsubst,
|
|
list(constructor_arg)).
|
|
:- mode substitute_type_args_3(in, in, out) is det.
|
|
|
|
substitute_type_args_3([], _, []).
|
|
substitute_type_args_3([Name - Arg0 | Args0], Subst, [Name - Arg | Args]) :-
|
|
term__apply_substitution(Arg0, Subst, Arg),
|
|
substitute_type_args_3(Args0, Subst, Args).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Check whether TypesA subsumes TypesB, and if so return
|
|
% a type substitution that will map from TypesA to TypesB.
|
|
|
|
type_list_subsumes(TypesA, TypesB, TypeSubst) :-
|
|
%
|
|
% TypesA subsumes TypesB iff TypesA can be unified with TypesB
|
|
% without binding any of the type variables in TypesB.
|
|
%
|
|
term__vars_list(TypesB, TypesBVars),
|
|
map__init(TypeSubst0),
|
|
type_unify_list(TypesA, TypesB, TypesBVars, TypeSubst0, TypeSubst).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Types are represented as terms, but we can't just use term__unify
|
|
% because we need to avoid binding any of the "head type params"
|
|
% (the type variables that occur in the head of the clause),
|
|
% and because one day we might want to handle equivalent types.
|
|
|
|
type_unify(term__variable(X), term__variable(Y), HeadTypeParams,
|
|
Bindings0, Bindings) :-
|
|
( list__member(Y, HeadTypeParams) ->
|
|
type_unify_head_type_param(X, Y, HeadTypeParams,
|
|
Bindings0, Bindings)
|
|
; list__member(X, HeadTypeParams) ->
|
|
type_unify_head_type_param(Y, X, HeadTypeParams,
|
|
Bindings0, Bindings)
|
|
; map__search(Bindings0, X, BindingOfX) ->
|
|
( map__search(Bindings0, Y, BindingOfY) ->
|
|
% both X and Y already have bindings - just
|
|
% unify the types they are bound to
|
|
type_unify(BindingOfX, BindingOfY, HeadTypeParams,
|
|
Bindings0, Bindings)
|
|
;
|
|
term__apply_rec_substitution(BindingOfX,
|
|
Bindings0, SubstBindingOfX),
|
|
% Y is a type variable which hasn't been bound yet
|
|
( SubstBindingOfX = term__variable(Y) ->
|
|
Bindings = Bindings0
|
|
;
|
|
\+ term__occurs(SubstBindingOfX, Y,
|
|
Bindings0),
|
|
map__det_insert(Bindings0, Y, SubstBindingOfX,
|
|
Bindings)
|
|
)
|
|
)
|
|
;
|
|
( map__search(Bindings0, Y, BindingOfY) ->
|
|
term__apply_rec_substitution(BindingOfY,
|
|
Bindings0, SubstBindingOfY),
|
|
% X is a type variable which hasn't been bound yet
|
|
( SubstBindingOfY = term__variable(X) ->
|
|
Bindings = Bindings0
|
|
;
|
|
\+ term__occurs(SubstBindingOfY, X,
|
|
Bindings0),
|
|
map__det_insert(Bindings0, X, SubstBindingOfY,
|
|
Bindings)
|
|
)
|
|
;
|
|
% both X and Y are unbound type variables -
|
|
% bind one to the other
|
|
( X = Y ->
|
|
Bindings = Bindings0
|
|
;
|
|
map__det_insert(Bindings0, X,
|
|
term__variable(Y), Bindings)
|
|
)
|
|
)
|
|
).
|
|
|
|
type_unify(term__variable(X), term__functor(F, As, C), HeadTypeParams,
|
|
Bindings0, Bindings) :-
|
|
(
|
|
map__search(Bindings0, X, BindingOfX)
|
|
->
|
|
type_unify(BindingOfX, term__functor(F, As, C),
|
|
HeadTypeParams, Bindings0, Bindings)
|
|
;
|
|
\+ term__occurs_list(As, X, Bindings0),
|
|
\+ list__member(X, HeadTypeParams),
|
|
map__det_insert(Bindings0, X, term__functor(F, As, C),
|
|
Bindings)
|
|
).
|
|
|
|
type_unify(term__functor(F, As, C), term__variable(X), HeadTypeParams,
|
|
Bindings0, Bindings) :-
|
|
(
|
|
map__search(Bindings0, X, BindingOfX)
|
|
->
|
|
type_unify(term__functor(F, As, C), BindingOfX,
|
|
HeadTypeParams, Bindings0, Bindings)
|
|
;
|
|
\+ term__occurs_list(As, X, Bindings0),
|
|
\+ list__member(X, HeadTypeParams),
|
|
map__det_insert(Bindings0, X, term__functor(F, As, C),
|
|
Bindings)
|
|
).
|
|
|
|
type_unify(term__functor(FX, AsX, _CX), term__functor(FY, AsY, _CY),
|
|
HeadTypeParams, Bindings0, Bindings) :-
|
|
list__length(AsX, ArityX),
|
|
list__length(AsY, ArityY),
|
|
(
|
|
FX = FY,
|
|
ArityX = ArityY
|
|
->
|
|
type_unify_list(AsX, AsY, HeadTypeParams, Bindings0, Bindings)
|
|
;
|
|
fail
|
|
).
|
|
|
|
% XXX Instead of just failing if the functors' name/arity is different,
|
|
% we should check here if these types have been defined
|
|
% to be equivalent using equivalence types. But this
|
|
% is difficult because (1) it causes typevarset synchronization
|
|
% problems, and (2) the relevant variables TypeInfo, TVarSet0, TVarSet
|
|
% haven't been passed in to here.
|
|
|
|
/*******
|
|
...
|
|
;
|
|
replace_eqv_type(FX, ArityX, AsX, EqvType)
|
|
->
|
|
type_unify(EqvType, term__functor(FY, AsY, CY),
|
|
HeadTypeParams, Bindings0, Bindings)
|
|
;
|
|
replace_eqv_type(FY, ArityY, AsY, EqvType)
|
|
->
|
|
type_unify(term__functor(FX, AsX, CX), EqvType,
|
|
HeadTypeParams, Bindings0, Bindings)
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- pred replace_eqv_type(const, int, list(type), type).
|
|
:- mode replace_eqv_type(in, in, in, out) is semidet.
|
|
|
|
replace_eqv_type(Functor, Arity, Args, EqvType) :-
|
|
|
|
% XXX magically_obtain(TypeTable, TVarSet0, TVarSet)
|
|
|
|
make_type_id(Functor, Arity, TypeId),
|
|
map__search(TypeTable, TypeId, TypeDefn),
|
|
TypeDefn = hlds_type_defn(TypeVarSet, TypeParams0,
|
|
eqv_type(EqvType0), _Condition, Context, _Status),
|
|
varset__merge(TVarSet0, TypeVarSet, [EqvType0 | TypeParams0],
|
|
TVarSet, [EqvType1, TypeParams1]),
|
|
type_param_to_var_list(TypeParams1, TypeParams),
|
|
term__substitute_corresponding(EqvType1, TypeParams, AsX,
|
|
EqvType).
|
|
|
|
******/
|
|
|
|
type_unify_list([], [], _) --> [].
|
|
type_unify_list([X | Xs], [Y | Ys], HeadTypeParams) -->
|
|
type_unify(X, Y, HeadTypeParams),
|
|
type_unify_list(Xs, Ys, HeadTypeParams).
|
|
|
|
:- pred type_unify_head_type_param(tvar, tvar, list(tvar), tsubst, tsubst).
|
|
:- mode type_unify_head_type_param(in, in, in, in, out) is semidet.
|
|
|
|
type_unify_head_type_param(Var, HeadVar, HeadTypeParams, Bindings0,
|
|
Bindings) :-
|
|
( map__search(Bindings0, Var, BindingOfVar) ->
|
|
BindingOfVar = term__variable(Var2),
|
|
type_unify_head_type_param(Var2, HeadVar, HeadTypeParams,
|
|
Bindings0, Bindings)
|
|
;
|
|
( Var = HeadVar ->
|
|
Bindings = Bindings0
|
|
;
|
|
\+ list__member(Var, HeadTypeParams),
|
|
map__det_insert(Bindings0, Var,
|
|
term__variable(HeadVar), Bindings)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
type_util__vars(Type, Tvars) :-
|
|
term__vars(Type, Tvars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_substitution_to_type_map(VarTypes0, Subst, VarTypes) :-
|
|
% optimize the common case of an empty type substitution
|
|
( map__is_empty(Subst) ->
|
|
VarTypes = VarTypes0
|
|
;
|
|
map__keys(VarTypes0, Vars),
|
|
apply_substitution_to_type_map_2(Vars, VarTypes0, Subst,
|
|
VarTypes)
|
|
).
|
|
|
|
:- pred apply_substitution_to_type_map_2(list(prog_var)::in,
|
|
map(prog_var, type)::in, tsubst::in, map(prog_var, type)::out)
|
|
is det.
|
|
|
|
apply_substitution_to_type_map_2([], VarTypes, _Subst, VarTypes).
|
|
apply_substitution_to_type_map_2([Var | Vars], VarTypes0, Subst,
|
|
VarTypes) :-
|
|
map__lookup(VarTypes0, Var, VarType0),
|
|
term__apply_substitution(VarType0, Subst, VarType),
|
|
map__det_update(VarTypes0, Var, VarType, VarTypes1),
|
|
apply_substitution_to_type_map_2(Vars, VarTypes1, Subst, VarTypes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_rec_substitution_to_type_map(VarTypes0, Subst, VarTypes) :-
|
|
% optimize the common case of an empty type substitution
|
|
( map__is_empty(Subst) ->
|
|
VarTypes = VarTypes0
|
|
;
|
|
map__keys(VarTypes0, Vars),
|
|
apply_rec_substitution_to_type_map_2(Vars, VarTypes0, Subst,
|
|
VarTypes)
|
|
).
|
|
|
|
:- pred apply_rec_substitution_to_type_map_2(list(prog_var)::in,
|
|
map(prog_var, type)::in, tsubst::in, map(prog_var, type)::out)
|
|
is det.
|
|
|
|
apply_rec_substitution_to_type_map_2([], VarTypes, _Subst, VarTypes).
|
|
apply_rec_substitution_to_type_map_2([Var | Vars], VarTypes0, Subst,
|
|
VarTypes) :-
|
|
map__lookup(VarTypes0, Var, VarType0),
|
|
term__apply_rec_substitution(VarType0, Subst, VarType),
|
|
map__det_update(VarTypes0, Var, VarType, VarTypes1),
|
|
apply_rec_substitution_to_type_map_2(Vars, VarTypes1, Subst, VarTypes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_substitutions_to_var_map(VarMap0, TRenaming, TSubst, Subst, VarMap) :-
|
|
% optimize the common case of empty substitutions
|
|
(
|
|
map__is_empty(Subst),
|
|
map__is_empty(TSubst),
|
|
map__is_empty(TRenaming)
|
|
->
|
|
VarMap = VarMap0
|
|
;
|
|
map__keys(VarMap0, TVars),
|
|
map__init(NewVarMap),
|
|
apply_substitutions_to_var_map_2(TVars, VarMap0,
|
|
TRenaming, TSubst, Subst, NewVarMap, VarMap)
|
|
).
|
|
|
|
|
|
:- pred apply_substitutions_to_var_map_2(list(tvar)::in, map(tvar,
|
|
type_info_locn)::in, tsubst::in, map(tvar, type)::in,
|
|
map(prog_var, prog_var)::in, map(tvar, type_info_locn)::in,
|
|
map(tvar, type_info_locn)::out) is det.
|
|
|
|
apply_substitutions_to_var_map_2([], _VarMap0, _, _, _, NewVarMap, NewVarMap).
|
|
apply_substitutions_to_var_map_2([TVar | TVars], VarMap0, TRenaming,
|
|
TSubst, VarSubst, NewVarMap0, NewVarMap) :-
|
|
map__lookup(VarMap0, TVar, Locn),
|
|
type_info_locn_var(Locn, Var),
|
|
|
|
% find the new var, if there is one
|
|
( map__search(VarSubst, Var, NewVar0) ->
|
|
NewVar = NewVar0
|
|
;
|
|
NewVar = Var
|
|
),
|
|
type_info_locn_set_var(Locn, NewVar, NewLocn),
|
|
|
|
% find the new tvar, if there is one, otherwise just
|
|
% create the old var as a type variable.
|
|
(
|
|
map__search(TRenaming, TVar, NewTVar0)
|
|
->
|
|
( NewTVar0 = term__variable(NewTVar1) ->
|
|
NewTVar2 = NewTVar1
|
|
;
|
|
% varset__merge_subst only returns var->var mappings,
|
|
% never var->term.
|
|
error(
|
|
"apply_substitution_to_var_map_2: weird type renaming")
|
|
)
|
|
;
|
|
% The variable wasn't renamed.
|
|
NewTVar2 = TVar
|
|
),
|
|
|
|
term__apply_rec_substitution(term__variable(NewTVar2),
|
|
TSubst, NewType),
|
|
|
|
% if the tvar is still a variable, insert it into the
|
|
% map with the new var.
|
|
( type_util__var(NewType, NewTVar) ->
|
|
% Don't abort if two old type variables
|
|
% map to the same new type variable.
|
|
map__set(NewVarMap0, NewTVar, NewLocn, NewVarMap1)
|
|
;
|
|
NewVarMap1 = NewVarMap0
|
|
),
|
|
apply_substitutions_to_var_map_2(TVars, VarMap0, TRenaming,
|
|
TSubst, VarSubst, NewVarMap1, NewVarMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_substitutions_to_typeclass_var_map(VarMap0,
|
|
TRenaming, TSubst, Subst, VarMap) :-
|
|
map__to_assoc_list(VarMap0, VarAL0),
|
|
list__map(apply_substitutions_to_typeclass_var_map_2(TRenaming,
|
|
TSubst, Subst), VarAL0, VarAL),
|
|
map__from_assoc_list(VarAL, VarMap).
|
|
|
|
:- pred apply_substitutions_to_typeclass_var_map_2(tsubst, map(tvar, type),
|
|
map(prog_var, prog_var), pair(class_constraint, prog_var),
|
|
pair(class_constraint, prog_var)).
|
|
:- mode apply_substitutions_to_typeclass_var_map_2(in, in,
|
|
in, in, out) is det.
|
|
|
|
apply_substitutions_to_typeclass_var_map_2(TRenaming, TSubst, VarRenaming,
|
|
Constraint0 - Var0, Constraint - Var) :-
|
|
apply_subst_to_constraint(TRenaming, Constraint0, Constraint1),
|
|
apply_rec_subst_to_constraint(TSubst, Constraint1, Constraint),
|
|
|
|
( map__search(VarRenaming, Var0, Var1) ->
|
|
Var = Var1
|
|
;
|
|
Var = Var0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_rec_subst_to_constraints(Subst, Constraints0, Constraints) :-
|
|
Constraints0 = constraints(UnivCs0, ExistCs0),
|
|
apply_rec_subst_to_constraint_list(Subst, UnivCs0, UnivCs),
|
|
apply_rec_subst_to_constraint_list(Subst, ExistCs0, ExistCs),
|
|
Constraints = constraints(UnivCs, ExistCs).
|
|
|
|
apply_rec_subst_to_constraint_list(Subst, Constraints0, Constraints) :-
|
|
list__map(apply_rec_subst_to_constraint(Subst), Constraints0,
|
|
Constraints).
|
|
|
|
apply_rec_subst_to_constraint(Subst, Constraint0, Constraint) :-
|
|
Constraint0 = constraint(ClassName, Types0),
|
|
term__apply_rec_substitution_to_list(Types0, Subst, Types1),
|
|
% we need to maintain the invariant that types in class constraints
|
|
% do not have any information in their prog_context fields
|
|
strip_prog_contexts(Types1, Types),
|
|
Constraint = constraint(ClassName, Types).
|
|
|
|
apply_subst_to_constraints(Subst,
|
|
constraints(UniversalCs0, ExistentialCs0),
|
|
constraints(UniversalCs, ExistentialCs)) :-
|
|
apply_subst_to_constraint_list(Subst, UniversalCs0, UniversalCs),
|
|
apply_subst_to_constraint_list(Subst, ExistentialCs0, ExistentialCs).
|
|
|
|
apply_subst_to_constraint_list(Subst, Constraints0, Constraints) :-
|
|
list__map(apply_subst_to_constraint(Subst), Constraints0, Constraints).
|
|
|
|
apply_subst_to_constraint(Subst, Constraint0, Constraint) :-
|
|
Constraint0 = constraint(ClassName, Types0),
|
|
term__apply_substitution_to_list(Types0, Subst, Types),
|
|
Constraint = constraint(ClassName, Types).
|
|
|
|
apply_subst_to_constraint_proofs(Subst, Proofs0, Proofs) :-
|
|
map__init(Empty),
|
|
map__foldl(
|
|
lambda([Constraint0::in, Proof0::in, Map0::in, Map::out] is det,
|
|
(
|
|
apply_subst_to_constraint(Subst, Constraint0,
|
|
Constraint),
|
|
(
|
|
Proof0 = apply_instance(_),
|
|
Proof = Proof0
|
|
;
|
|
Proof0 = superclass(Super0),
|
|
apply_subst_to_constraint(Subst, Super0,
|
|
Super),
|
|
Proof = superclass(Super)
|
|
),
|
|
map__set(Map0, Constraint, Proof, Map)
|
|
)),
|
|
Proofs0, Empty, Proofs).
|
|
|
|
apply_rec_subst_to_constraint_proofs(Subst, Proofs0, Proofs) :-
|
|
map__init(Empty),
|
|
map__foldl(
|
|
lambda([Constraint0::in, Proof0::in, Map0::in, Map::out] is det,
|
|
(
|
|
apply_rec_subst_to_constraint(Subst, Constraint0,
|
|
Constraint),
|
|
(
|
|
Proof0 = apply_instance(_),
|
|
Proof = Proof0
|
|
;
|
|
Proof0 = superclass(Super0),
|
|
apply_rec_subst_to_constraint(Subst, Super0,
|
|
Super),
|
|
Proof = superclass(Super)
|
|
),
|
|
map__set(Map0, Constraint, Proof, Map)
|
|
)),
|
|
Proofs0, Empty, Proofs).
|
|
|
|
apply_variable_renaming_to_constraints(Renaming,
|
|
constraints(UniversalCs0, ExistentialCs0),
|
|
constraints(UniversalCs, ExistentialCs)) :-
|
|
apply_variable_renaming_to_constraint_list(Renaming,
|
|
UniversalCs0, UniversalCs),
|
|
apply_variable_renaming_to_constraint_list(Renaming,
|
|
ExistentialCs0, ExistentialCs).
|
|
|
|
apply_variable_renaming_to_constraint_list(Renaming, Constraints0,
|
|
Constraints) :-
|
|
list__map(apply_variable_renaming_to_constraint(Renaming),
|
|
Constraints0, Constraints).
|
|
|
|
apply_variable_renaming_to_constraint(Renaming, Constraint0, Constraint) :-
|
|
Constraint0 = constraint(ClassName, ClassArgTypes0),
|
|
term__apply_variable_renaming_to_list(ClassArgTypes0,
|
|
Renaming, ClassArgTypes),
|
|
Constraint = constraint(ClassName, ClassArgTypes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
apply_partial_map_to_list([], _PartialMap, []).
|
|
apply_partial_map_to_list([X|Xs], PartialMap, [Y|Ys]) :-
|
|
( map__search(PartialMap, X, Y0) ->
|
|
Y = Y0
|
|
;
|
|
Y = X
|
|
),
|
|
apply_partial_map_to_list(Xs, PartialMap, Ys).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
strip_prog_contexts(Terms, StrippedTerms) :-
|
|
list__map(strip_prog_context, Terms, StrippedTerms).
|
|
|
|
strip_prog_context(term__variable(V), term__variable(V)).
|
|
strip_prog_context(term__functor(F, As0, _C0),
|
|
term__functor(F, As, C)) :-
|
|
term__context_init(C),
|
|
strip_prog_contexts(As0, As).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|