mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
533 lines
19 KiB
Mathematica
533 lines
19 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2016-2022, 2024 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This module defines the types that represent information related to pragmas
|
|
% in the parse tree.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.prog_data_pragma.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.rat.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module one_or_more.
|
|
:- import_module pair.
|
|
:- import_module set.
|
|
:- import_module unit.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module require.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for tabling pragmas.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
% The evaluation method that should be used for a procedure.
|
|
%
|
|
:- type eval_method
|
|
---> eval_normal % normal mercury evaluation
|
|
; eval_tabled(tabled_eval_method). % tabled evaluation
|
|
|
|
% The evaluation method that should be used for a procedure.
|
|
%
|
|
:- type tabled_eval_method
|
|
---> tabled_loop_check
|
|
% loop check only
|
|
; tabled_memo(
|
|
% memoing + loop check
|
|
% Preserve the value of this attribute until the invocation
|
|
% of the relevant code in table_gen.m.
|
|
table_attr_backend_warning
|
|
)
|
|
; tabled_io(
|
|
% memoing I/O actions for debugging
|
|
table_io_entry_kind,
|
|
table_io_is_unitize
|
|
)
|
|
; tabled_minimal(
|
|
% minimal model evaluation
|
|
eval_minimal_method
|
|
).
|
|
|
|
:- type eval_minimal_method
|
|
---> stack_copy
|
|
% Each minimal model procedure saves and restores stack segments
|
|
% as necessary. See the paper "Tabling in Mercury" by Zoltan
|
|
% Somogyi and Konstantinos Sagonas.
|
|
|
|
; own_stacks_consumer
|
|
; own_stacks_generator.
|
|
% Each minimal model procedure is split into two: the consumer
|
|
% and the generator. Each generator runs in its own context,
|
|
% and thus has its own stacks.
|
|
|
|
:- type table_attributes
|
|
---> table_attributes(
|
|
table_attr_strictness :: call_table_strictness,
|
|
table_attr_size_limit :: maybe(int),
|
|
table_attr_statistics :: table_attr_statistics,
|
|
table_attr_allow_reset :: table_attr_allow_reset,
|
|
table_attr_backend_warning :: table_attr_backend_warning
|
|
).
|
|
|
|
:- func default_memo_table_attributes = table_attributes.
|
|
|
|
:- type table_attr_statistics
|
|
---> table_do_not_gather_statistics
|
|
; table_gather_statistics.
|
|
|
|
:- type table_attr_allow_reset
|
|
---> table_do_not_allow_reset
|
|
; table_allow_reset.
|
|
|
|
% If the current backend cannot implement the requested form of tabling,
|
|
% and is therefore forced to ignore it, should the compiler generate
|
|
% a warning?
|
|
:- type table_attr_backend_warning
|
|
---> table_attr_ignore_with_warning % Yes, generate a warning.
|
|
; table_attr_ignore_without_warning. % Do not generate a warning.
|
|
|
|
:- type call_table_strictness
|
|
---> cts_all_strict
|
|
; cts_all_fast_loose
|
|
; cts_specified(
|
|
list(maybe(arg_tabling_method)),
|
|
% This list contains one element for each user-visible
|
|
% argument of the predicate. Elements that correspond
|
|
% to output arguments should be "no". Elements that
|
|
% correspond to input arguments should be "yes",
|
|
% specifying how to look up that argument in the call table.
|
|
|
|
hidden_arg_tabling_method
|
|
% This specifies the tabling method for hidden arguments
|
|
% introduced by the compiler.
|
|
).
|
|
|
|
:- type arg_tabling_method
|
|
---> arg_value
|
|
; arg_addr
|
|
; arg_promise_implied.
|
|
|
|
:- type hidden_arg_tabling_method
|
|
---> table_hidden_arg_value
|
|
; table_hidden_arg_addr.
|
|
|
|
:- type table_io_entry_kind
|
|
---> entry_stores_outputs
|
|
% Each entry in the I/O table stores only the outputs of the
|
|
% action. The I/O action will be idempotent across retries
|
|
% in mdb, but attempts to print out the action will cause
|
|
% a core dump. This option is intended only for implementors
|
|
% measuring the overheads of the two alternatives just below.
|
|
|
|
; entry_stores_procid_outputs
|
|
% Each entry in the I/O table starts with a pointer to the
|
|
% MR_TableIoEntry structure of the procedure that performed
|
|
% the action, and also contains the outputs of the action.
|
|
% This makes the I/O action idempotent across retries and
|
|
% allows the *name* of the I/O predicate to be printed
|
|
% by mdb's "print action N" command, but not the values
|
|
% of the arguments. Not even the output arguments can be printed,
|
|
% since doing so requires knowing their types, and in general
|
|
% that requires access to input type_info arguments.
|
|
|
|
; entry_stores_procid_inputs_outputs.
|
|
% Each entry in the I/O table starts with a pointer to the
|
|
% MR_TableIoEntry structure of the procedure that performed
|
|
% the action, and also contains both the inputs and outputs
|
|
% of the action.
|
|
%
|
|
% This makes the I/O action idempotent across retries and
|
|
% allows both the name and all the arguments of the I/O predicate
|
|
% to be printed by mdb's "print action N" command. It also
|
|
% allows the declarative debugger to consider the action to
|
|
% be part of the effect of a call to its ancestors.
|
|
|
|
:- type table_io_is_unitize
|
|
---> table_io_unitize % The procedure is tabled for I/O
|
|
% together with its Mercury descendants.
|
|
|
|
; table_io_alone. % The procedure is tabled for I/O by itself;
|
|
% it can have no Mercury descendants.
|
|
|
|
:- func tabled_eval_method_to_table_type(tabled_eval_method) = string.
|
|
|
|
:- implementation.
|
|
|
|
default_memo_table_attributes =
|
|
table_attributes(cts_all_strict, no, table_do_not_gather_statistics,
|
|
table_do_not_allow_reset, table_attr_ignore_with_warning).
|
|
|
|
tabled_eval_method_to_table_type(EvalMethod) = TableTypeStr :-
|
|
(
|
|
EvalMethod = tabled_io(_, _),
|
|
unexpected($pred, "eval_table_io")
|
|
;
|
|
EvalMethod = tabled_loop_check,
|
|
TableTypeStr = "MR_TABLE_TYPE_LOOPCHECK"
|
|
;
|
|
EvalMethod = tabled_memo(_),
|
|
TableTypeStr = "MR_TABLE_TYPE_MEMO"
|
|
;
|
|
EvalMethod = tabled_minimal(stack_copy),
|
|
TableTypeStr = "MR_TABLE_TYPE_MINIMAL_MODEL_STACK_COPY"
|
|
;
|
|
EvalMethod = tabled_minimal(own_stacks_consumer),
|
|
unexpected($pred, "own_stacks_consumer")
|
|
;
|
|
EvalMethod = tabled_minimal(own_stacks_generator),
|
|
TableTypeStr = "MR_TABLE_TYPE_MINIMAL_MODEL_OWN_STACKS"
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `termination_info' pragma.
|
|
% See term_util.m.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type generic_arg_size_info(ErrorInfo)
|
|
---> 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(ErrorInfo).
|
|
% There is no finite integer for which the above equation is true.
|
|
|
|
:- type generic_termination_info(TermInfo, ErrorInfo)
|
|
---> cannot_loop(TermInfo)
|
|
% This procedure definitely terminates for all possible inputs.
|
|
; can_loop(ErrorInfo).
|
|
% This procedure might not terminate.
|
|
|
|
:- type pragma_arg_size_info == generic_arg_size_info(unit).
|
|
:- type pragma_termination_info == generic_termination_info(unit, unit).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `termination2_info' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
% This is the form in which termination information from other
|
|
% modules (imported via `.opt' or `.trans_opt' files) comes.
|
|
% We convert this to an intermediate form and let the termination
|
|
% analyser convert it to the correct form.
|
|
%
|
|
% NOTE: the reason that we cannot convert it to the correct form
|
|
% is that we don't have complete information about how many typeinfo
|
|
% related arguments there are until after the polymorphism pass.
|
|
%
|
|
:- type arg_size_constr
|
|
---> le(list(arg_size_term), rat)
|
|
; eq(list(arg_size_term), rat).
|
|
|
|
:- type arg_size_term
|
|
---> arg_size_term(
|
|
as_term_var :: int,
|
|
as_term_coeff :: rat
|
|
).
|
|
|
|
:- type pragma_constr_arg_size_info == list(arg_size_constr).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `structure_sharing_info' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
% Whenever structure sharing analysis is unable to determine a good
|
|
% approximation of the set of structure sharing pairs that might exist
|
|
% during the execution of a program, it must use "top" as the only safe
|
|
% approximation.
|
|
%
|
|
% We divide the reasons for approximating by `top' into two cases:
|
|
%
|
|
% - the procedure calls some imported procedure for which we don't have an
|
|
% answer (yet). The result might be improved if we did have that
|
|
% information.
|
|
%
|
|
% - the procedure calls some imported procedure for which we managed to
|
|
% look up the answer, and that answer was `top'.
|
|
%
|
|
% - the procedure contains a call to foreign or generic code.
|
|
% Reanalysis will not improve the result.
|
|
%
|
|
:- type top_feedback
|
|
---> top_failed_lookup(shrouded_pred_proc_id)
|
|
; top_from_lookup(shrouded_pred_proc_id)
|
|
; top_cannot_improve(string).
|
|
|
|
% Elements of the structure sharing domain lattice are either bottom
|
|
% (no structure sharing), top (any kind of structure sharing), or
|
|
% a list of structure sharing pairs.
|
|
%
|
|
% This is the public representation of the type "sharing_as".
|
|
%
|
|
:- type structure_sharing_domain
|
|
---> structure_sharing_bottom
|
|
; structure_sharing_real(structure_sharing)
|
|
; structure_sharing_top(set(top_feedback)).
|
|
|
|
% Public representation of structure sharing.
|
|
%
|
|
:- type structure_sharing == list(structure_sharing_pair).
|
|
|
|
% A structure sharing pair represents the information that two
|
|
% data structures might be represented by the same memoryspace, hence
|
|
% its representation as a pair of datastructs.
|
|
%
|
|
:- type structure_sharing_pair == pair(datastruct).
|
|
|
|
% A datastruct is a concept that designates a particular subterm of the
|
|
% term to which a particular variable may be bound. The selector is
|
|
% normalized.
|
|
%
|
|
:- type datastruct
|
|
---> selected_cel(
|
|
sc_var :: prog_var,
|
|
sc_selector :: selector
|
|
).
|
|
|
|
% A selector describes a path in a type-tree.
|
|
%
|
|
:- type selector == list(unit_selector).
|
|
|
|
% Unit-selectors are either term selectors or type selectors.
|
|
% - A term selector selects a subterm f/n of a term, where f is a functor
|
|
% (identified by the cons_id), and n an integer.
|
|
% - A type selector designates any subterm that has that specific type.
|
|
%
|
|
:- type unit_selector
|
|
---> termsel(cons_id, int) % term selector
|
|
; typesel(mer_type). % type selector
|
|
|
|
% Type to represent the sharing information that is manually added
|
|
% to procedures implemented as foreign_procs.
|
|
%
|
|
:- type user_annotated_sharing
|
|
---> no_user_annotated_sharing
|
|
; user_sharing(
|
|
sharing :: structure_sharing_domain,
|
|
maybe_types :: maybe(user_sharing_type_information)
|
|
).
|
|
|
|
% The user may have declared the sharing in terms of type variables. In
|
|
% that case, we record the types, and the type variable set.
|
|
%
|
|
:- type user_sharing_type_information
|
|
---> user_type_info(
|
|
types :: list(mer_type),
|
|
typevarset :: tvarset
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `structure_reuse_info' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type dead_var == prog_var.
|
|
:- type dead_datastruct == datastruct.
|
|
:- type dead_datastructs == set(dead_datastruct).
|
|
:- type live_var == prog_var.
|
|
:- type set_of_live_var == set_of_progvar.
|
|
:- type live_datastruct == datastruct.
|
|
|
|
% This is the public representation of the type "reuse_as".
|
|
%
|
|
:- type structure_reuse_domain
|
|
---> has_no_reuse
|
|
; has_only_unconditional_reuse
|
|
; has_conditional_reuse(list(structure_reuse_condition)).
|
|
|
|
% A structure reuse condition specifies all the information needed to
|
|
% verify whether some memory cells can safely be considered as dead at
|
|
% some program point, depending on the calling context.
|
|
% This information consists of three parts:
|
|
% - a list of dead datastructures specifying which memory cells
|
|
% might become dead, hence reusable;
|
|
% - a list of live datastructures that specifies which memory cells
|
|
% are always live at the place where the above dead datastructures might
|
|
% become dead;
|
|
% - a description of the structure sharing existing at the place
|
|
% where these datastructures might become dead.
|
|
%
|
|
:- type structure_reuse_condition
|
|
---> structure_reuse_condition(
|
|
dead_nodes :: dead_datastructs,
|
|
local_use_nodes :: list(live_datastruct),
|
|
local_sharing :: structure_sharing_domain
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `unused_args' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
% This `mode_num' type is only used for mode numbers written out in
|
|
% automatically-generated `pragma unused_args' pragmas in `.opt' files.
|
|
% The mode_num gets converted to an HLDS proc_id by make_hlds.m.
|
|
% We don't want to use the `proc_id' type here since the parse tree
|
|
% (prog_data.m and prog_item.m) should not depend on the HLDS.
|
|
%
|
|
:- type mode_num == int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `exceptions' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type exception_status
|
|
---> will_not_throw
|
|
% This procedure will not throw an exception.
|
|
|
|
; may_throw(exception_type)
|
|
% This procedure may throw an exception. The exception is
|
|
% classified by the `exception_type' type.
|
|
|
|
; throw_conditional.
|
|
% Whether the procedure will not throw an exception depends upon
|
|
% the value of one or more polymorphic arguments. XXX This needs
|
|
% to be extended for ho preds. (See exception_analysis.m for
|
|
% more details).
|
|
|
|
:- type exception_type
|
|
---> user_exception
|
|
% The exception that might be thrown is of a result of some code
|
|
% calling exception.throw/1.
|
|
|
|
; type_exception.
|
|
% The exception is a result of a compiler introduced
|
|
% unification/comparison maybe throwing an exception
|
|
% (in the case of user-defined equality or comparison) or
|
|
% propagating an exception from them.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `format_call' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type format_string_values
|
|
---> format_string_values(
|
|
% The format_call pragma allows users to specify pairs of
|
|
% argument numbers, the first specifying the arg containing
|
|
% the format string, the second the arg containing the
|
|
% values list (as a list of poly_types).
|
|
|
|
% The first pair of arguments contain the argument numbers
|
|
% given by the user. The first user-visible arg is arg 1,
|
|
% the second is arg 2, and so on.
|
|
%
|
|
% All error messages generated for invalid argument numbers
|
|
% use this pair.
|
|
fsv_user_fs :: int,
|
|
fsv_user_vl :: int,
|
|
|
|
% The second pair of arguments is derived from the first pair,
|
|
% but are incremented by the number of any additional
|
|
% type_info and/or typeclass_info arguments added by the
|
|
% polymorphism pass. The code of check_pragma_format_call.m
|
|
% finds the argument types and modes to check by indexing
|
|
% into argument lists using these numbers.
|
|
fsv_cur_fs :: int,
|
|
fsv_cur_vl :: int
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `type_spec' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
% The type substitution for a `pragma type_spec' declaration.
|
|
% Elsewhere in the compiler we generally use the `tsubst' type
|
|
% which is a map rather than (effectively) an assoc_list.
|
|
%
|
|
:- type type_subst == one_or_more(tvar_subst).
|
|
:- type tvar_subst
|
|
---> tvar_subst(tvar, mer_type).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for the `require_feature_set' pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type required_feature
|
|
---> reqf_concurrency
|
|
; reqf_single_prec_float
|
|
; reqf_double_prec_float
|
|
; reqf_memo
|
|
; reqf_parallel_conj
|
|
; reqf_trailing
|
|
; reqf_strict_sequential
|
|
; reqf_conservative_gc.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Require tail recursion pragma.
|
|
%
|
|
|
|
:- interface.
|
|
|
|
:- type require_tail_recursion
|
|
---> suppress_tailrec_warnings(
|
|
rtrs_context :: prog_context
|
|
)
|
|
; enable_tailrec_warnings(
|
|
rtre_warn_or_error :: warning_or_error,
|
|
rtre_recursion_type :: require_tail_recursion_type,
|
|
rtre_context :: prog_context
|
|
).
|
|
|
|
:- type require_tail_recursion_type
|
|
---> only_self_recursion_must_be_tail
|
|
; both_self_and_mutual_recursion_must_be_tail.
|
|
|
|
:- pred require_tailrec_type_string(require_tail_recursion_type, string).
|
|
:- mode require_tailrec_type_string(in, out) is det.
|
|
:- mode require_tailrec_type_string(out, in) is semidet.
|
|
|
|
:- implementation.
|
|
|
|
require_tailrec_type_string(only_self_recursion_must_be_tail,
|
|
"self_recursion_only").
|
|
require_tailrec_type_string(both_self_and_mutual_recursion_must_be_tail,
|
|
"self_or_mutual_recursion").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.prog_data_pragma.
|
|
%---------------------------------------------------------------------------%
|