Files
mercury/compiler/term_util.m
Fergus Henderson 11d8161692 Add support for nested modules.
Estimated hours taken: 50

Add support for nested modules.

- module names may themselves be module-qualified
- modules may contain `:- include_module' declarations
  which name sub-modules
- a sub-module has access to all the declarations in the
  parent module (including its implementation section).

This support is not yet complete; see the BUGS and LIMITATIONS below.

LIMITATIONS
- source file names must match module names
	(just as they did previously)
- mmc doesn't allow path names on the command line any more
	(e.g. `mmc --make-int ../library/foo.m').
- import_module declarations must use the fully-qualified module name
- module qualifiers must use the fully-qualified module name
- no support for root-qualified module names
	(e.g. `:parent:child' instead of `parent:child').
- modules may not be physically nested (only logical nesting, via
  `include_module').

BUGS
- doesn't check that the parent module is imported/used before allowing
	import/use of its sub-modules.
- doesn't check that there is an include_module declaration in the
	parent for each module claiming to be a child of that parent
- privacy of private modules is not enforced

-------------------

NEWS:
	Mention that we support nested modules.

library/ops.m:
library/nc_builtin.nl:
library/sp_builtin.nl:
compiler/mercury_to_mercury.m:
	Add `include_module' as a new prefix operator.
	Change the associativity of `:' from xfy to yfx
	(since this made parsing module qualifiers slightly easier).

compiler/prog_data.m:
	Add new `include_module' declaration.
	Change the `module_name' and `module_specifier' types
	from strings to sym_names, so that module names can
	themselves be module qualified.

compiler/modules.m:
	Add predicates module_name_to_file_name/2 and
	file_name_to_module_name/2.
	Lots of changes to handle parent module dependencies,
	to create parent interface (`.int0') files, to read them in,
	to output correct dependencies information for them to the
	`.d' and `.dep' files, etc.
	Rewrite a lot of the code to improve the readability
	(add comments, use subroutines, better variable names).
	Also fix a couple of bugs:
	- generate_dependencies was using the transitive implementation
	  dependencies rather than the transitive interface dependencies
	  to compute the `.int3' dependencies when writing `.d' files
	  (this bug was introduced during crs's changes to support
	  `.trans_opt' files)
	- when creating the `.int' file, it was reading in the
	  interfaces for modules imported in the implementation section,
	  not just those in the interface section.
	  This meant that the compiler missed a lot of errors.

library/graph.m:
library/lexer.m:
library/term.m:
library/term_io.m:
library/varset.m:
compiler/*.m:
	Add `:- import_module' declarations to the interface needed
	by declarations in the interface.  (The previous version
	of the compiler did not detect these missing interface imports,
	due to the above-mentioned bug in modules.m.)

compiler/mercury_compile.m:
compiler/intermod.m:
	Change mercury_compile__maybe_grab_optfiles and
	intermod__grab_optfiles so that they grab the opt files for
	parent modules as well as the ones for imported modules.

compiler/mercury_compile.m:
	Minor changes to handle parent module dependencies.
	(Also improve the wording of the warning about trans-opt
	dependencies.)

compiler/make_hlds.m:
compiler/module_qual.m:
	Ignore `:- include_module' declarations.

compiler/module_qual.m:
	A couple of small changes to handle nested module names.

compiler/prog_out.m:
compiler/prog_util.m:
	Add new predicates string_to_sym_name/3 (prog_util.m) and
	sym_name_to_string/{2,3} (prog_out.m).

compiler/*.m:
	Replace many occurrences of `string' with `module_name'.
	Change code that prints out module names or converts
	them to strings or filenames to handle the fact that
	module names are now sym_names intead of strings.
	Also change a few places (e.g. in intermod.m, hlds_module.m)
	where the code assumed that any qualified symbol was
	fully-qualified.

compiler/prog_io.m:
compiler/prog_io_goal.m:
	Move sym_name_and_args/3, parse_qualified_term/4 and
	parse_qualified_term/5 preds from prog_io_goal.m to prog_io.m,
	since they are very similar to the parse_symbol_name/2 predicate
	already in prog_io.m.  Rewrite these predicates, both
	to improve maintainability, and to handle the newly
	allowed syntax (module-qualified module names).
	Rename parse_qualified_term/5 as `parse_implicit_qualified_term'.

compiler/prog_io.m:
	Rewrite the handling of `:- module' and `:- end_module'
	declarations, so that it can handle nested modules.
	Add code to parse `include_module' declarations.

compiler/prog_util.m:
compiler/*.m:
	Add new predicates mercury_public_builtin_module/1 and
	mercury_private_builtin_module/1 in prog_util.m.
	Change most of the hard-coded occurrences of "mercury_builtin"
	to call mercury_private_builtin_module/1 or
	mercury_public_builtin_module/1 or both.

compiler/llds_out.m:
	Add llds_out__sym_name_mangle/2, for mangling module names.

compiler/special_pred.m:
compiler/mode_util.m:
compiler/clause_to_proc.m:
compiler/prog_io_goal.m:
compiler/lambda.m:
compiler/polymorphism.m:
	Move the predicates in_mode/1, out_mode/1, and uo_mode/1
	from special_pred.m to mode_util.m, and change various
	hard-coded definitions to instead call these predicates.

compiler/polymorphism.m:
	Ensure that the type names `type_info' and `typeclass_info' are
	module-qualified in the generated code.  This avoids a problem
	where the code generated by polymorphism.m was not considered
	type-correct, due to the type `type_info' not matching
	`mercury_builtin:type_info'.

compiler/check_typeclass.m:
	Simplify the code for check_instance_pred and
	get_matching_instance_pred_ids.

compiler/mercury_compile.m:
compiler/modules.m:
	Disallow directory names in command-line arguments.

compiler/options.m:
compiler/handle_options.m:
compiler/mercury_compile.m:
compiler/modules.m:
	Add a `--make-private-interface' option.
	The private interface file `<module>.int0' contains
	all the declarations in the module; it is used for
	compiling sub-modules.

scripts/Mmake.rules:
scripts/Mmake.vars.in:
	Add support for creating `.int0' and `.date0' files
	by invoking mmc with `--make-private-interface'.

doc/user_guide.texi:
	Document `--make-private-interface' and the `.int0'
	and `.date0' file extensions.

doc/reference_manual.texi:
	Document nested modules.

util/mdemangle.c:
profiler/demangle.m:
	Demangle names with multiple module qualifiers.

tests/general/Mmakefile:
tests/general/string_format_test.m:
tests/general/string_format_test.exp:
tests/general/string__format_test.m:
tests/general/string__format_test.exp:
tests/general/.cvsignore:
	Change the `:- module string__format_test' declaration in
	`string__format_test.m' to `:- module string_format_test',
	because with the original declaration the `__' was taken
	as a module qualifier, which lead to an error message.
	Hence rename the file accordingly, to avoid the warning
	about file name not matching module name.

tests/invalid/Mmakefile:
tests/invalid/missing_interface_import.m:
tests/invalid/missing_interface_import.err_exp:
	Regression test to check that the compiler reports
	errors for missing `import_module' in the interface section.

tests/invalid/*.err_exp:
tests/warnings/unused_args_test.exp:
tests/warnings/unused_import.exp:
	Update the expected diagnostics output for the test cases to
	reflect a few minor changes to the warning messages.

tests/hard_coded/Mmakefile:
tests/hard_coded/parent.m:
tests/hard_coded/parent.child.m:
tests/hard_coded/parent.exp:
tests/hard_coded/parent2.m:
tests/hard_coded/parent2.child.m:
tests/hard_coded/parent2.exp:
	Two simple tests case for the use of nested modules with
	separate compilation.
1998-03-03 17:48:14 +00:00

555 lines
19 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1997-1998 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.
%-----------------------------------------------------------------------------%
%
% term_util.m
% Main author: crs.
%
% This module:
%
% - defines the types used by termination analysis
% - defines predicates for computing functor norms
% - defines some utility predicates
%
%-----------------------------------------------------------------------------%
:- module term_util.
:- interface.
:- import_module term_errors, prog_data.
:- import_module hlds_module, hlds_pred, hlds_data, hlds_goal.
:- import_module std_util, bool, int, list, map, bag, term.
%-----------------------------------------------------------------------------%
% The arg size info defines an upper bound on the difference
% between the sizes of the output arguments of a procedure and the sizes
% of the input arguments:
%
% | input arguments | + constant >= | output arguments |
%
% where | | represents a semilinear norm.
:- type arg_size_info
---> finite(int, list(bool))
% The termination constant is a finite integer.
% The list of bool has a 1:1 correspondence
% with the input arguments of the procedure.
% It stores whether the argument contributes
% to the size of the output arguments.
; infinite(list(term_errors__error)).
% There is no finite integer for which the
% above equation is true. The argument says
% why the analysis failed to find a finite
% constant.
:- type termination_info
---> cannot_loop % This procedure terminates for all
% possible inputs.
; can_loop(list(term_errors__error)).
% The analysis could not prove that the
% procedure terminates.
:- type used_args == map(pred_proc_id, list(bool)).
%-----------------------------------------------------------------------------%
% We use semilinear norms (denoted by ||) to compute the sizes of terms.
% These have the form
%
% | f(t1, ... tn) | = weight(f) + sum of | ti |
% where i is an element of a set I, and I is a subset of {1, ... n}
%
% We currently support four kinds of semilinear norms.
:- type functor_info
---> simple % All non-constant functors have weight 1,
% while constants have weight 0.
% Use the size of all subterms (I = {1, ..., n}.
; total % All functors have weight = arity of the functor.
% Use the size of all subterms (I = {1, ..., n}.
; use_map(weight_table)
% The weight of each functor is given by the table.
% Use the size of all subterms (I = {1, ..., n}.
; use_map_and_args(weight_table).
% The weight of each functor is given by the table,
% and so is the set of arguments of the functor whose
% size should be counted (I is given by the table
% entry of the functor).
:- type unify_info == pair(map(var, type), functor_info).
:- type weight_info ---> weight(int, list(bool)).
:- type weight_table == map(pair(type_id, cons_id), weight_info).
:- pred find_weights(module_info::in, weight_table::out) is det.
% This predicate is computes the weight of a functor and the set of arguments
% of that functor whose sizes should be counted towards the size of the whole
% term.
:- pred functor_norm(functor_info::in, type_id::in, cons_id::in,
module_info::in, int::out, list(var)::in, list(var)::out,
list(uni_mode)::in, list(uni_mode)::out) is det.
:- type pass_info
---> pass_info(
functor_info,
int, % Max number of errors to gather.
int % Max number of paths to analyze.
).
%-----------------------------------------------------------------------------%
% This predicate partitions the arguments of a call into a list of input
% variables and a list of output variables,
:- pred partition_call_args(module_info::in, list(mode)::in, list(var)::in,
bag(var)::out, bag(var)::out) is det.
% Given a list of variables from a unification, this predicate divides the
% list into a bag of input variables, and a bag of output variables.
:- pred split_unification_vars(list(var)::in, list(uni_mode)::in,
module_info::in, bag(var)::out, bag(var)::out) is det.
% Used to create lists of boolean values, which are used for used_args.
% make_bool_list(HeadVars, BoolIn, BoolOut) creates a bool list which is
% (length(HeadVars) - length(BoolIn)) `no' followed by BoolIn. This is
% used to set the used args for compiler generated predicates. The no's
% at the start are because the Type infos are not used. length(BoolIn)
% should equal the arity of the predicate, and the difference in length
% between the arity of the procedure and the arity of the predicate is
% the number of type infos.
:- pred term_util__make_bool_list(list(_T)::in, list(bool)::in,
list(bool)::out) is det.
% Removes variables from the InVarBag that are not used in the call.
% remove_unused_args(InVarBag0, VarList, BoolList, InVarBag)
% VarList and BoolList are corresponding lists. Any variable in VarList
% that has a `no' in the corresponding place in the BoolList is removed
% from InVarBag.
:- pred remove_unused_args(bag(var), list(var), list(bool), bag(var)).
:- mode remove_unused_args(in, in, in, out) is det.
% This predicate sets the argument size info of a given a list of procedures.
:- pred set_pred_proc_ids_arg_size_info(list(pred_proc_id)::in,
arg_size_info::in, module_info::in, module_info::out) is det.
% This predicate sets the termination info of a given a list of procedures.
:- pred set_pred_proc_ids_termination_info(list(pred_proc_id)::in,
termination_info::in, module_info::in, module_info::out) is det.
:- pred lookup_proc_termination_info(module_info::in, pred_proc_id::in,
maybe(termination_info)::out) is det.
:- pred lookup_proc_arg_size_info(module_info::in, pred_proc_id::in,
maybe(arg_size_info)::out) is det.
% Succeeds if one or more variables in the list are higher order.
:- pred horder_vars(list(var), map(var, type)).
:- mode horder_vars(in, in) is semidet.
% Succeeds if all values of the given type are zero size (for all norms).
:- pred zero_size_type(type, module_info).
:- mode zero_size_type(in, in) is semidet.
:- pred get_context_from_scc(list(pred_proc_id)::in, module_info::in,
term__context::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module inst_match, prog_out, mode_util, type_util.
:- import_module globals, options.
:- import_module assoc_list, require.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Calculate the weight to be assigned to each function symbol for the
% use_map and use_map_and_args semilinear norms.
%
% Given a type definition such as
%
% :- type t(Tk) ---> f1(a11, ... a1n1) where n1 is the arity of f1
% ; ...
% ; fm(am1, ... amnm) where nm is the arity of fm
%
% we check, for each aij, whether its type is recursive (i.e. it is t with
% type variable arguments that are a permutation of Tk). The weight info
% we compute for each functor will have a boolean list that has a `yes'
% for each recursive argument and a `no' for each nonrecursive argument.
% The weight to be assigned to the functor is the number of nonrecursive
% arguments, except that we assign a weight of at least 1 to all functors
% which are not constants.
find_weights(ModuleInfo, Weights) :-
module_info_types(ModuleInfo, TypeTable),
map__to_assoc_list(TypeTable, TypeList),
map__init(Weights0),
find_weights_for_type_list(TypeList, Weights0, Weights).
:- pred find_weights_for_type_list(assoc_list(type_id, hlds_type_defn)::in,
weight_table::in, weight_table::out) is det.
find_weights_for_type_list([], Weights, Weights).
find_weights_for_type_list([TypeId - TypeDefn | TypeList], Weights0, Weights) :-
find_weights_for_type(TypeId, TypeDefn, Weights0, Weights1),
find_weights_for_type_list(TypeList, Weights1, Weights).
:- pred find_weights_for_type(type_id::in, hlds_type_defn::in,
weight_table::in, weight_table::out) is det.
find_weights_for_type(TypeId, TypeDefn, Weights0, Weights) :-
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
(
TypeBody = du_type(Constructors, _, _, _),
hlds_data__get_type_defn_tparams(TypeDefn, TypeParams),
find_weights_for_cons_list(Constructors, TypeId, TypeParams,
Weights0, Weights)
;
TypeBody = uu_type(_),
error("undiscriminated union types not yet implemented")
;
% This type does not introduce any functors
TypeBody = eqv_type(_),
Weights = Weights0
;
% This type may introduce some functors,
% but we will never see them in this analysis
TypeBody = abstract_type,
Weights = Weights0
).
:- pred find_weights_for_cons_list(list(constructor)::in,
type_id::in, list(type_param)::in,
weight_table::in, weight_table::out) is det.
find_weights_for_cons_list([], _, _, Weights, Weights).
find_weights_for_cons_list([Constructor | Constructors], TypeId, Params,
Weights0, Weights) :-
find_weights_for_cons(Constructor, TypeId, Params, Weights0, Weights1),
find_weights_for_cons_list(Constructors, TypeId, Params,
Weights1, Weights).
:- pred find_weights_for_cons(constructor::in,
type_id::in, list(type_param)::in,
weight_table::in, weight_table::out) is det.
find_weights_for_cons(SymName - Args, TypeId, Params, Weights0, Weights) :-
list__length(Args, Arity),
( Arity > 0 ->
find_and_count_nonrec_args(Args, TypeId, Params,
NumNonRec, ArgInfos0),
( NumNonRec = 0 ->
Weight = 1,
list__duplicate(Arity, yes, ArgInfos)
;
Weight = NumNonRec,
ArgInfos = ArgInfos0
),
WeightInfo = weight(Weight, ArgInfos)
;
WeightInfo = weight(0, [])
),
ConsId = cons(SymName, Arity),
map__det_insert(Weights0, TypeId - ConsId, WeightInfo, Weights).
:- pred find_and_count_nonrec_args(list(constructor_arg)::in,
type_id::in, list(type_param)::in,
int::out, list(bool)::out) is det.
find_and_count_nonrec_args([], _, _, 0, []).
find_and_count_nonrec_args([Arg | Args], Id, Params, NonRecArgs, ArgInfo) :-
find_and_count_nonrec_args(Args, Id, Params, NonRecArgs0, ArgInfo0),
( is_arg_recursive(Arg, Id, Params) ->
NonRecArgs = NonRecArgs0,
ArgInfo = [yes | ArgInfo0]
;
NonRecArgs is NonRecArgs0 + 1,
ArgInfo = [no | ArgInfo0]
).
:- pred is_arg_recursive(constructor_arg::in,
type_id::in, list(type_param)::in) is semidet.
is_arg_recursive(Arg, Id, Params) :-
Arg = _Name - ArgType,
type_to_type_id(ArgType, ArgTypeId, ArgTypeParams),
Id = ArgTypeId,
list__perm(Params, ArgTypeParams).
%-----------------------------------------------------------------------------%
% Although the module info is not used in either of these norms, it could
% be needed for other norms, so it should not be removed.
functor_norm(simple, _, ConsId, _, Int, Args, Args, Modes, Modes) :-
(
ConsId = cons(_, Arity),
Arity \= 0
->
Int = 1
;
Int = 0
).
functor_norm(total, _, ConsId, _Module, Int, Args, Args, Modes, Modes) :-
( ConsId = cons(_, Arity) ->
Int = Arity
;
Int = 0
).
functor_norm(use_map(WeightMap), TypeId, ConsId, _Module, Int,
Args, Args, Modes, Modes) :-
( map__search(WeightMap, TypeId - ConsId, WeightInfo) ->
WeightInfo = weight(Int, _)
;
Int = 0
).
functor_norm(use_map_and_args(WeightMap), TypeId, ConsId, _Module, Int,
Args0, Args, Modes0, Modes) :-
( map__search(WeightMap, TypeId - ConsId, WeightInfo) ->
WeightInfo = weight(Int, UseArgList),
(
functor_norm_filter_args(UseArgList, Args0, Args1,
Modes0, Modes1)
->
Modes = Modes1,
Args = Args1
;
error("Unmatched lists in functor_norm_filter_args.")
)
;
Int = 0,
Modes = Modes0,
Args = Args0
).
% This predicate will fail if the length of the input lists are not matched.
:- pred functor_norm_filter_args(list(bool), list(var), list(var),
list(uni_mode), list(uni_mode)).
:- mode functor_norm_filter_args(in, in, out, in, out) is semidet.
functor_norm_filter_args([], [], [], [], []).
functor_norm_filter_args([yes | Bools], [Arg0 | Args0], [Arg0 | Args],
[Mode0 | Modes0], [Mode0 | Modes]) :-
functor_norm_filter_args(Bools, Args0, Args, Modes0, Modes).
functor_norm_filter_args([no | Bools], [_Arg0 | Args0], Args,
[_Mode0 | Modes0], Modes) :-
functor_norm_filter_args(Bools, Args0, Args, Modes0, Modes).
%-----------------------------------------------------------------------------%
partition_call_args(Module, ArgModes, Args, InVarsBag, OutVarsBag) :-
partition_call_args_2(Module, ArgModes, Args, InVars, OutVars),
bag__from_list(InVars, InVarsBag),
bag__from_list(OutVars, OutVarsBag).
:- pred partition_call_args_2(module_info::in, list(mode)::in, list(var)::in,
list(var)::out, list(var)::out) is det.
partition_call_args_2(_, [], [], [], []).
partition_call_args_2(_, [], [_ | _], _, _) :-
error("Unmatched variables in term_util:partition_call_args").
partition_call_args_2(_, [_ | _], [], _, _) :-
error("Unmatched variables in term_util__partition_call_args").
partition_call_args_2(ModuleInfo, [ArgMode | ArgModes], [Arg | Args],
InputArgs, OutputArgs) :-
partition_call_args_2(ModuleInfo, ArgModes, Args,
InputArgs1, OutputArgs1),
( mode_is_input(ModuleInfo, ArgMode) ->
InputArgs = [Arg | InputArgs1],
OutputArgs = OutputArgs1
; mode_is_output(ModuleInfo, ArgMode) ->
InputArgs = InputArgs1,
OutputArgs = [Arg | OutputArgs1]
;
InputArgs = InputArgs1,
OutputArgs = OutputArgs1
).
% For these next two predicates (split_unification_vars and
% partition_call_args) there is a problem of what needs to be done for
% partially instantiated data structures. The correct answer is that the
% system shoud use a norm such that the size of the uninstantiated parts of
% a partially instantiated structure have no effect on the size of the data
% structure according to the norm. For example when finding the size of a
% list-skeleton, list-length norm should be used. Therefore, the size of
% any term must be given by
% sizeof(term) = constant + sum of the size of each
% (possibly partly) instantiated subterm.
% It is probably easiest to implement this by modifying term_weights.
% The current implementation does not correctly handle partially
% instantiated data structures.
split_unification_vars([], Modes, _ModuleInfo, Vars, Vars) :-
bag__init(Vars),
( Modes = [] ->
true
;
error("term_util:split_unification_vars: Unmatched Variables")
).
split_unification_vars([Arg | Args], Modes, ModuleInfo,
InVars, OutVars):-
( Modes = [UniMode | UniModes] ->
split_unification_vars(Args, UniModes, ModuleInfo,
InVars0, OutVars0),
UniMode = ((_VarInit - ArgInit) -> (_VarFinal - ArgFinal)),
( % if
inst_is_bound(ModuleInfo, ArgInit)
->
% Variable is an input variable
bag__insert(InVars0, Arg, InVars),
OutVars = OutVars0
; % else if
inst_is_free(ModuleInfo, ArgInit),
inst_is_bound(ModuleInfo, ArgFinal)
->
% Variable is an output variable
InVars = InVars0,
bag__insert(OutVars0, Arg, OutVars)
; % else
InVars = InVars0,
OutVars = OutVars0
)
;
error("term_util__split_unification_vars: Unmatched Variables")
).
%-----------------------------------------------------------------------------%
term_util__make_bool_list(HeadVars0, Bools, Out) :-
list__length(Bools, Arity),
( list__drop(Arity, HeadVars0, HeadVars1) ->
HeadVars = HeadVars1
;
error("Unmatched variables in term_util:make_bool_list")
),
term_util__make_bool_list_2(HeadVars, Bools, Out).
:- pred term_util__make_bool_list_2(list(_T), list(bool), list(bool)).
:- mode term_util__make_bool_list_2(in, in, out) is det.
term_util__make_bool_list_2([], Bools, Bools).
term_util__make_bool_list_2([ _ | Vars ], Bools, [no | Out]) :-
term_util__make_bool_list_2(Vars, Bools, Out).
remove_unused_args(Vars, [], [], Vars).
remove_unused_args(Vars, [], [_X | _Xs], Vars) :-
error("Unmatched variables in term_util:remove_unused_args").
remove_unused_args(Vars, [_X | _Xs], [], Vars) :-
error("Unmatched variables in term_util__remove_unused_args").
remove_unused_args(Vars0, [ Arg | Args ], [ UsedVar | UsedVars ], Vars) :-
( UsedVar = yes ->
% The variable is used, so leave it
remove_unused_args(Vars0, Args, UsedVars, Vars)
;
% The variable is not used in producing output vars, so
% dont include it as an input variable.
bag__delete(Vars0, Arg, Vars1),
remove_unused_args(Vars1, Args, UsedVars, Vars)
).
%-----------------------------------------------------------------------------%
set_pred_proc_ids_arg_size_info([], _ArgSize, Module, Module).
set_pred_proc_ids_arg_size_info([PPId | PPIds], ArgSize, Module0, Module) :-
PPId = proc(PredId, ProcId),
module_info_preds(Module0, PredTable0),
map__lookup(PredTable0, PredId, PredInfo0),
pred_info_procedures(PredInfo0, ProcTable0),
map__lookup(ProcTable0, ProcId, ProcInfo0),
proc_info_set_maybe_arg_size_info(ProcInfo0, yes(ArgSize), ProcInfo),
map__det_update(ProcTable0, ProcId, ProcInfo, ProcTable),
pred_info_set_procedures(PredInfo0, ProcTable, PredInfo),
map__det_update(PredTable0, PredId, PredInfo, PredTable),
module_info_set_preds(Module0, PredTable, Module1),
set_pred_proc_ids_arg_size_info(PPIds, ArgSize, Module1, Module).
set_pred_proc_ids_termination_info([], _Termination, Module, Module).
set_pred_proc_ids_termination_info([PPId | PPIds], Termination,
Module0, Module) :-
PPId = proc(PredId, ProcId),
module_info_preds(Module0, PredTable0),
map__lookup(PredTable0, PredId, PredInfo0),
pred_info_procedures(PredInfo0, ProcTable0),
map__lookup(ProcTable0, ProcId, ProcInfo0),
proc_info_set_maybe_termination_info(ProcInfo0, yes(Termination),
ProcInfo),
map__det_update(ProcTable0, ProcId, ProcInfo, ProcTable),
pred_info_set_procedures(PredInfo0, ProcTable, PredInfo),
map__det_update(PredTable0, PredId, PredInfo, PredTable),
module_info_set_preds(Module0, PredTable, Module1),
set_pred_proc_ids_termination_info(PPIds, Termination,
Module1, Module).
lookup_proc_termination_info(Module, PredProcId, MaybeTermination) :-
PredProcId = proc(PredId, ProcId),
module_info_pred_proc_info(Module, PredId, ProcId, _, ProcInfo),
proc_info_get_maybe_termination_info(ProcInfo, MaybeTermination).
lookup_proc_arg_size_info(Module, PredProcId, MaybeArgSize) :-
PredProcId = proc(PredId, ProcId),
module_info_pred_proc_info(Module, PredId, ProcId, _, ProcInfo),
proc_info_get_maybe_arg_size_info(ProcInfo, MaybeArgSize).
horder_vars([Arg | Args], VarType) :-
(
map__lookup(VarType, Arg, Type),
type_is_higher_order(Type, _, _)
;
horder_vars(Args, VarType)
).
zero_size_type(Type, Module) :-
classify_type(Type, Module, TypeCategory),
zero_size_type_category(TypeCategory, Type, Module, yes).
:- pred zero_size_type_category(builtin_type, type, module_info, bool).
:- mode zero_size_type_category(in, in, in, out) is det.
zero_size_type_category(int_type, _, _, yes).
zero_size_type_category(char_type, _, _, yes).
zero_size_type_category(str_type, _, _, yes).
zero_size_type_category(float_type, _, _, yes).
zero_size_type_category(pred_type, _, _, no).
zero_size_type_category(enum_type, _, _, yes).
zero_size_type_category(polymorphic_type, _, _, no).
zero_size_type_category(user_type, _, _, no).
%-----------------------------------------------------------------------------%
get_context_from_scc(SCC, Module, Context) :-
( SCC = [proc(PredId, _) | _] ->
module_info_pred_info(Module, PredId, PredInfo),
pred_info_context(PredInfo, Context)
;
error("Empty SCC in pass 2 of termination analysis")
).
%-----------------------------------------------------------------------------%