Files
mercury/compiler/post_term_analysis.m
Zoltan Somogyi 77adfe5439 Add all the copyright year updates that were left off in my previous commit
Estimated hours taken: 0.1
Branches: main

Add all the copyright year updates that were left off in my previous commit
over the ext protocol.
2012-06-08 15:46:21 +00:00

251 lines
9.7 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%----------------------------------------------------------------------------%
% Copyright (C) 2005-2007,2009-2012 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: post_term_analysis.m.
% Main author: juliensf.
%
% This module contains various checks that rely on the information produced by
% termination analysis.
%
% Currently, the only thing implemented in this module is a check to see if
% user-defined special predicates terminate. A warning is emitted for all
% those that do not.
%
%----------------------------------------------------------------------------%
:- module transform_hlds.post_term_analysis.
:- interface.
:- import_module hlds.hlds_module.
:- import_module io.
:- pred run_post_term_analysis(module_info::in, io::di, io::uo) is det.
%----------------------------------------------------------------------------%
%----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.foreign.
:- import_module hlds.goal_form.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_out.
:- import_module hlds.hlds_out.hlds_out_util.
:- import_module hlds.hlds_pred.
:- import_module hlds.special_pred.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_type.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module string.
%----------------------------------------------------------------------------%
run_post_term_analysis(ModuleInfo, !IO) :-
warn_non_term_user_special_preds(ModuleInfo, !IO).
%----------------------------------------------------------------------------%
%
% Warn about user-defined special predicates that do not terminate.
%
% We check the termination status of user-defined special predicates
% by taking the body goal of the compiler generated wrapper predicate
% and checking if that terminates. We cannot check the termination status
% of the compiler generated wrappers directly, because termination analysis
% always assumes that they terminate.
%
% Since all of the special predicates of interest here have to be defined
% in the same module as the type that uses them, we only check locally
% defined types. The ones for imported types will be checked when
% the relevant module is compiled and analysed.
%
:- pred warn_non_term_user_special_preds(module_info::in, io::di, io::uo)
is det.
warn_non_term_user_special_preds(ModuleInfo, !IO) :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, termination, Termination),
globals.lookup_bool_option(Globals, warn_non_term_special_preds,
WarnSpecialPreds),
globals.lookup_bool_option(Globals, make_optimization_interface,
MakeOptInt),
globals.lookup_bool_option(Globals, transitive_optimization,
TransIntermodOpt),
(
Termination = yes, WarnSpecialPreds = yes,
%
% Don't run this pass if we are only building the optimization
% interface and we are compiling with
% `--transitive-intermodule-optimization' enabled because we'll get
% more accurate results when we build the .trans_opt files. Any
% warnings this time around may be spurious.
%
not (MakeOptInt = yes, TransIntermodOpt = yes)
->
module_info_get_special_pred_map(ModuleInfo, SpecialPredMap),
module_info_get_type_table(ModuleInfo, TypeTable),
map.foldl(warn_non_term_user_special_pred(ModuleInfo, TypeTable),
SpecialPredMap, !IO)
;
true
).
:- pred warn_non_term_user_special_pred(module_info::in, type_table::in,
special_pred::in, pred_id::in, io::di, io::uo) is det.
warn_non_term_user_special_pred(ModuleInfo, TypeTable,
SpecialPredId - TypeCtor, PredId, !IO) :-
% Index predicates cannot be defined by the user and should always
% terminate by design. Do not perform this check for builtin types
% that don't have hlds_type_defns.
BuiltinTypeCtors = builtin_type_ctors_with_no_hlds_type_defn,
(
SpecialPredId \= spec_pred_index,
not list.member(TypeCtor, BuiltinTypeCtors)
->
lookup_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
get_type_defn_status(TypeDefn, ImportStatus),
DefinedThisModule = status_defined_in_this_module(ImportStatus),
(
DefinedThisModule = yes,
process_special_pred_for_type(ModuleInfo,
SpecialPredId, TypeCtor, PredId, TypeDefn, !IO)
;
DefinedThisModule = no
)
;
true
).
% If the specified special predicate for the given type is user-defined
% then check that it terminates. Emit a warning if it does not.
%
:- pred process_special_pred_for_type(module_info::in,
special_pred_id::in, type_ctor::in, pred_id::in,
hlds_type_defn::in, io::di, io::uo) is det.
process_special_pred_for_type(ModuleInfo, SpecialPredId, TypeCtor,
PredId, TypeDefn, !IO) :-
( special_pred_needs_term_check(ModuleInfo, SpecialPredId, TypeDefn) ->
% Compiler generated special preds are always mode 0.
proc_id_to_int(ProcId, 0),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo),
proc_info_get_goal(ProcInfo, BodyGoal0),
% The pretest code we add for compiler-generated unification
% and comparison predicates uses type casts. It uses them in a way
% that is guaranteed to terminate, but our analysis is not (yet) able
% to find this out for itself. We therefore analyse only the
% non-pretest parts of such goals.
BodyGoal = maybe_strip_equality_pretest(BodyGoal0),
% We cannot just look up the the termination_info because the
% termination status of compiler generated wrapper predicates for
% special preds is always set to terminates. Instead, we check if the
% body of the generated wrapper predicate terminates.
( not goal_cannot_loop(ModuleInfo, BodyGoal) ->
get_type_defn_context(TypeDefn, Context),
module_info_get_globals(ModuleInfo, Globals),
emit_non_term_user_special_warning(Globals, Context, SpecialPredId,
TypeCtor, !IO)
;
true
)
;
true
).
% Succeeds if the specified type of special_pred for this type needs to
% have its termination status checked.
%
:- pred special_pred_needs_term_check(module_info::in,
special_pred_id::in, hlds_type_defn::in) is semidet.
special_pred_needs_term_check(ModuleInfo, SpecialPredId, TypeDefn) :-
get_type_defn_body(TypeDefn, TypeBody),
(
% Always check solver type initialisation
% predicates since they are always user-defined.
SpecialPredId = spec_pred_init
;
get_user_unify_compare(ModuleInfo, TypeBody, UnifyCompare),
(
UnifyCompare = unify_compare(MaybeUnify, MaybeCompare),
(
MaybeUnify = yes(_),
SpecialPredId = spec_pred_unify
;
MaybeCompare = yes(_),
SpecialPredId = spec_pred_compare
)
;
UnifyCompare = abstract_noncanonical_type(_),
unexpected($module, $pred,
"type is local and abstract_noncanonical")
)
).
% Succeeds if the given type has user-defined equality and/or comparison
% and returns the relevant information about which predicates implement it.
%
:- pred get_user_unify_compare(module_info::in, hlds_type_body::in,
unify_compare::out) is semidet.
get_user_unify_compare(_ModuleInfo, TypeBody, UnifyCompare) :-
TypeBody = hlds_du_type(_, _, _, _, yes(UnifyCompare), _, _, _, _).
get_user_unify_compare(ModuleInfo, TypeBody, UnifyCompare) :-
TypeBody = hlds_foreign_type(ForeignTypeBody),
foreign_type_body_has_user_defined_eq_comp_pred(ModuleInfo,
ForeignTypeBody, UnifyCompare).
get_user_unify_compare(_ModuleInfo, TypeBody, UnifyCompare) :-
TypeBody = hlds_solver_type(_, yes(UnifyCompare)).
:- pred emit_non_term_user_special_warning(globals::in, prog_context::in,
special_pred_id::in, type_ctor::in, io::di, io::uo) is det.
emit_non_term_user_special_warning(Globals, Context, SpecialPred, TypeCtor,
!IO) :-
TypeCtorString = type_ctor_to_string(TypeCtor),
(
SpecialPred = spec_pred_unify,
SpecialPredStr = "equality"
;
SpecialPred = spec_pred_compare,
SpecialPredStr = "comparison"
;
SpecialPred = spec_pred_init,
SpecialPredStr = "initialisation"
;
SpecialPred = spec_pred_index,
unexpected($module, $pred, "index predicate.")
),
Pieces = [words("Warning: the user-defined "),
fixed(SpecialPredStr ++ " predicate"),
words("for the type "), fixed(TypeCtorString),
words("cannot be proven to terminate.")],
report_warning(Globals, Context, 0, Pieces, !IO).
%----------------------------------------------------------------------------%
:- end_module transform_hlds.post_term_analysis.
%----------------------------------------------------------------------------%