Files
mercury/compiler/term_constr_errors.m
Julien Fischer c5e315b0fc Add the first part of the new termination analyser.
Estimated hours taken: lots
Branches: main

Add the first part of the new termination analyser.  This includes most
of the argument size analyser and a simple termination checker - I'll
add a more extensive one as part of another diff.  The one included here
is only a placeholder and is roughly equivalent to the one used by the
existing termination analyser.

The new analysis abstracts term size relationships over a domain of
(rational) convex constraints.  The resulting size relationships are, in
many cases, more precise than we can obtain with the old analysis.  This
means that we should be able to prove termination in more cases.  The
tradeoff for this increased precision is that the new termination
analysis is slower than the old one.  We should also be able to adapt
the new analyser to handle things like higher-order constructs and
intermodule mutual recursion more easily than the old one.

The code for writing termination2_info pragmas to .opt and .trans_opt
files is currently commented out.  It will need to stay that way until
after this change bootstraps and after the tests/term directory as been
reworked (the .trans_opt_exp files need to updated and some new test
cases have to added - I'll do this as part of separate diff).

It isn't clear what the relationship between the new analyser and the
existing one should be, so at the moment they are as independent of each
other as possible.

compiler/termination2.m:
	New file.  Invokes the the other passes of the new analysis
	and handles the output of termination2_info pragmas to .opt
	and .trans_opt files.

	XXX I've disabled the writing out of termination2_info
	pragmas to the (transitive-)intermodule optimization files
	until I've updated the test suite.

compiler/term_constr_data.m:
	New file.  Defines the main data structures used by the
	new analysis.

compiler/term_constr_initial.m:
	New file.  Sets up information for builtin and compiler
	generated procedures and processes information about
	imported procedures.  Also handles termination pragmas.

compiler/term_constr_build.m:
	New file.  Converts the HLDS representation of a procedure
	into the abstract representation that we use during the rest
	of the analysis.

compiler/term_constr_fixpoint.m:
	New file.  Perform a fixpoint calculation in order to derive
	interargument size relationships for a procedure (in the
	form of convex constraints).

compiler/term_constr_pass2.m:
	New file.  A rudimentary termination checker that is roughly
	equivalent to what the existing termination analysis does.
	This is just a placeholder.

compiler/term_constr_util.m:
	New file.  Utility predicates that are used by the above modules.

compiler/term_constr_errors.m:
	New file.  Equivalent to term_errors.m for the new analyser.

compiler/rat.m:
	Provide rational numbers over fixed precision integers.
	Originally committed on the termination2 branch.

compiler/lp_rational.m:
	Provides the necessary machinery for manipulating systems
	of linear constraints.  Originally committed on the termination2
	branch although most of this version is new.  (Some bits of the
	version on the termination2 branch are now in polyhedron.m).
	The linear solver is essentially the one that is currently
	in lp.m converted to use rationals rather than floats.

compiler/polyhedron.m:
	New file.  An ADT that provides convex polyhedra over the
	rationals (or at least over rats).  The abstraction barrier was
	designed so that we could experiment with different representations
	for the polyhedra.

compiler/term_norm:
	Clean up the documentation of this module.
	Make set_functor_info into a function.
	Add a function for finding a lower bound on the weight of a functor.

compiler/trans_opt.m:
	Output termination2_info pragmas in .trans_opt files.

compiler/transform_hlds.m:
	Include the new termination analyser.

compiler/goal_form.m:
	When checking whether a goal can loop or not use
	information from the new termination analyser as well as
	the old one.

compiler/globals.m:
compiler/handle_options:
compiler/options.m:
	Add options to control the new analyser.   The documentation
	is currently commented out because the new analysis is still
	a work-in-progress.  It should be uncommented when the new
	analysis is more mature.

	XXX The user guide still needs to be updated.

compiler/hlds_out.m:
	Add hlds_out.write_pred_proc_id/4.

compiler/hlds_pred.m:
	Add a slot in the proc_sub_info structure for the
	termination2_info structure.

compiler/rat.m:
	Provide rational numbers over fixed precision integers.

compiler/lp_rational.m:
	Provide the constraint machinery required by the analyser.

compiler/make_hlds.m:
	Handle imports of termination2_info pragmas.

compiler/mercury_compile.m:
	Run the new pass.  Currently, we do this directly after
	the old termination analysis pass.

compiler/mercury_to_mercury.m:
	Add code to output termination2_info pragmas.

compiler/libs.m:
	Include the rat, polyhedron and lp_rational modules.

compiler/prog_data.m:
	Define the types necessary for termination2_info pragmas.

	Change the cannot_loop constructor of the generic_termination_info
	type so it has a polymorphic argument.  The new analyser stores
	information in it.

	Fix some spelling errors in some of the comments

compiler/prog_io_pragma.m:
	Parse termination2_info pragmas.

compiler/error_util.m:
	Add function versions of sorry/2 and unexpected/2.

compiler/module_qual.m:
compiler/modules.m:
compiler/recompilation.version.m:
compiler/termination.m
compiler/term_pass2.m
compiler/term_util.m:
	Minor changes to confrom to the above.
2005-04-07 06:32:17 +00:00

280 lines
8.8 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2002, 2005 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_constr_errors.m
% main author: juliensf.
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.term_constr_errors.
:- interface.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.prog_data.
:- import_module io.
:- import_module list.
:- import_module std_util.
%-----------------------------------------------------------------------------%
%
% Termination 2 Errors
%
% The termination errors are all in reference to possible non-termination.
% While it is possible for pass 1 to go amiss the worst that will happen
% (barring an abnormal abort) is that the size of the arguments will be
% unconstrained.
:- type termination2_error
---> imported_pred
% Termination could not be proved because
% it depends upon information from another
% module and that information is not
% available.
; can_loop_proc_called(pred_proc_id, pred_proc_id)
% Termination could not be proved because
% the procedure called another procedure
% that may not terminate.
; cond_not_satisfied
% Termination could not be proved because
% no set of decreasing argument could be found.
; horder_call
% Termination could not be proved because
% the procedure makes higher-order calls.
; does_not_term_pragma(pred_id)
% Termination could not be proved because
% the procedure was marked with a
% `does_not_terminate' pragma.
; foreign_proc_called(pred_proc_id).
% Termination depends upon the properties
% of a piece of foreign code that cannot
% be established as terminating.
:- type term_constr_errors.error == pair(prog_context, termination2_error).
:- type term2_errors == list(term_constr_errors.error).
%-----------------------------------------------------------------------------%
:- pred report_termination2_errors(list(pred_proc_id)::in, term2_errors::in,
module_info::in, module_info::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_error_util.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module parse_tree.error_util.
:- import_module transform_hlds.term_util. % for get_context_from_scc/5
:- import_module bool.
:- import_module int.
:- import_module require.
:- import_module string.
%-----------------------------------------------------------------------------%
report_termination2_errors(SCC, Errors, !Module, !IO) :-
globals.io_lookup_bool_option(check_termination2, NormalErrors, !IO),
globals.io_lookup_bool_option(verbose_check_termination2,
VerboseErrors, !IO),
(
IsCheckTerm = (pred(PPId::in) is semidet :-
module_info_pred_proc_info(!.Module, PPId, PredInfo, _),
not pred_info_is_imported(PredInfo),
pred_info_get_markers(PredInfo, Markers),
check_marker(Markers, check_termination)
),
CheckTermPPIds = list.filter(IsCheckTerm, SCC),
list.is_not_empty(CheckTermPPIds)
->
report_term_errors(SCC, Errors, !.Module, !IO),
io.set_exit_status(1, !IO),
module_info_incr_errors(!Module)
;
IsNonImported = (pred(PPId::in) is semidet :-
module_info_pred_proc_info(!.Module, PPId, PredInfo, _),
not pred_info_is_imported(PredInfo)
),
NonImportedPPIds = list.filter(IsNonImported, SCC),
list.is_not_empty(NonImportedPPIds),
( VerboseErrors = yes ->
PrintErrors = Errors
; NormalErrors = yes ->
IsNonSimple = (pred(ContextError::in) is semidet :-
ContextError = _ - Error,
not indirect_error(Error)
),
PrintErrors0 = list.filter(IsNonSimple, Errors),
%
% If there are no direct errors report
% the indirect ones instead.
%
( if PrintErrors0 = []
then PrintErrors = Errors
else PrintErrors = PrintErrors0
)
;
fail
)
->
term_constr_errors.report_term_errors(SCC, PrintErrors,
!.Module, !IO)
;
true
).
%-----------------------------------------------------------------------------%
:- pred report_term_errors(list(pred_proc_id)::in, term2_errors::in,
module_info::in, io::di, io::uo) is det.
report_term_errors(SCC, Errors, Module, !IO) :-
get_context_from_scc(SCC, Module, Context),
( SCC = [PPId] ->
Pieces0 = [words("Termination of")],
ProcName = describe_one_proc_name(Module, should_module_qualify,
PPId),
Pieces1 = Pieces0 ++ ProcName,
Single = yes(PPId)
;
Pieces0 = [
words("Termination of the mutually"),
words("recursive procedures")
],
ProcNames = describe_several_proc_names(Module,
should_module_qualify, SCC),
Pieces1 = Pieces0 ++ ProcNames,
Single = no
),
(
Errors = [],
Pieces2 = [words("not proven, for unknown reason(s).")],
write_error_pieces(Context, 0, Pieces1 ++ Pieces2, !IO)
;
Errors = [Error],
Pieces2 = [words("not proven for the following reason:")],
write_error_pieces(Context, 0, Pieces1 ++ Pieces2, !IO),
output_error(Error, Single, no, 0, Module, !IO)
;
Errors = [_, _ | _],
Pieces2 = [words("not proven for the following reasons:")],
write_error_pieces(Context, 0, Pieces1 ++ Pieces2, !IO),
output_errors(Errors, Single, 1, 0, Module, !IO)
).
:- pred output_errors(term2_errors::in,
maybe(pred_proc_id)::in, int::in, int::in, module_info::in,
io::di, io::uo) is det.
output_errors([], _, _, _, _, !IO).
output_errors([Error | Errors], Single, ErrNum0, Indent, Module, !IO) :-
output_error(Error, Single, yes(ErrNum0), Indent, Module, !IO),
output_errors(Errors, Single, ErrNum0 + 1, Indent, Module, !IO).
:- pred output_error(term_constr_errors.error::in, maybe(pred_proc_id)::in,
maybe(int)::in, int::in, module_info::in, io::di, io::uo) is det.
output_error(Context - Error, Single, ErrorNum, Indent, Module, !IO) :-
description(Error, Single, Module, Pieces0, _),
( ErrorNum = yes(N) ->
string.int_to_string(N, Nstr),
string.append_list(["Reason ", Nstr, ":"], Preamble),
Pieces = [fixed(Preamble) | Pieces0]
;
Pieces = Pieces0
),
write_error_pieces(Context, Indent, Pieces, !IO).
:- pred description(termination2_error::in,
maybe(pred_proc_id)::in, module_info::in, list(format_component)::out,
maybe(pred_proc_id)::out) is det.
description(cond_not_satisfied, _, _, Pieces, no) :-
Pieces = [
words("The termination condition"),
words("is not satisfiable.")
].
description(imported_pred, _, _, Pieces, no) :-
Pieces = [
words("It contains one or more"),
words("predicates and/or functions"),
words("imported from another module.")
].
description(can_loop_proc_called(CallerPPId, CalleePPId),
Single, Module, Pieces, no) :-
(
Single = yes(PPId),
require(unify(PPId, CallerPPId), "caller outside this SCC"),
Piece1 = [words("It")]
;
Single = no,
ProcName = describe_one_proc_name(Module, should_module_qualify,
CallerPPId),
Piece1 = ProcName
),
Piece2 = words("calls"),
CalleePiece = describe_one_proc_name(Module, should_module_qualify,
CalleePPId),
Pieces3 = [words("which could not be proven to terminate.")],
Pieces = Piece1 ++ [Piece2] ++ CalleePiece ++ Pieces3.
description(horder_call, _, _, Pieces, no) :-
Pieces = [words("It contains a higher-order call.")].
description(does_not_term_pragma(PredId), Single, Module, Pieces, no) :-
Pieces1 = [
words("There is a"),
fixed("`:- pragma does_not_terminate'"),
words("declaration for")
],
(
Single = yes(PPId),
PPId = proc(SCCPredId, _),
require(unify(PredId, SCCPredId),
"does not terminate pragma outside this SCC"),
Piece2 = [words("it.")]
;
Single = no,
Piece2Nodot = describe_one_pred_name(Module,
should_module_qualify, PredId),
Piece2 = Piece2Nodot ++ [words(".")]
),
Pieces = Pieces1 ++ Piece2.
description(foreign_proc_called(PPId), _Single, Module, Pieces, no) :-
Name = describe_one_proc_name(Module, should_module_qualify, PPId),
Pieces = [words("There is a call the foreign procedure")] ++
Name ++ [words("which is not known to terminate.")].
%-----------------------------------------------------------------------------%
:- pred indirect_error(termination2_error::in) is semidet.
indirect_error(imported_pred).
indirect_error(horder_call).
indirect_error(does_not_term_pragma(_)).
indirect_error(can_loop_proc_called(_, _)).
%-----------------------------------------------------------------------------%
:- end_module term_constr_errors.
%-----------------------------------------------------------------------------%