[m-rev.] for review 2/2: Reduce false positives for --warn-non-tail-recursive
Paul Bone
paul at bone.id.au
Thu Oct 29 16:15:32 AEDT 2015
For review by anyone
--
Reduce false positives for --warn-non-tail-recursive
This change applies only to the MLDS backends.
compiler/ml_tailcall.m:
As we traverse the MLDS backwards, if we encounter a recursive call
(whether it's a tailcall or not) don't create a warning for subsequent
tailcalls.
---
compiler/ml_tailcall.m | 77 +++++++++++++++++++++++++++++++++++++-------------
1 file changed, 58 insertions(+), 19 deletions(-)
diff --git a/compiler/ml_tailcall.m b/compiler/ml_tailcall.m
index 6dc1606..16caa37 100644
--- a/compiler/ml_tailcall.m
+++ b/compiler/ml_tailcall.m
@@ -105,11 +105,36 @@ ml_mark_tailcalls(Globals, !MLDS, !IO) :-
%-----------------------------------------------------------------------------%
+ % The algorithm works by walking backwards through each function in the
+ % MLDS. It tracks (via at_tail) whether the current position in the
+ % function's body is a tail position.
+
% The `at_tail' type indicates whether or not a subgoal is at a tail
% position, i.e. is followed by a return statement or the end of the
% function, and if so, specifies the return values (if any) in the return
% statement.
-:- type at_tail == maybe(list(mlds_rval)).
+:- type at_tail
+ ---> yes(list(mlds_rval))
+ ; no(seen_reccall).
+
+ % seen_retcall is used to track whether a recursive call has been seen.
+ % If it has we don't create warnings for further recursive calls. We
+ % continue to walk through the function incase we encounter a return
+ % statement and therefore find more tailcalls.
+ %
+:- type seen_reccall
+ ---> seen_reccall
+ ; have_not_seen_reccall.
+
+:- pred seen_reccall(at_tail::in, seen_reccall::out) is det.
+
+seen_reccall(yes(_), have_not_seen_reccall).
+seen_reccall(no(SeenReccall), SeenReccall).
+
+:- pred not_at_tail(at_tail::in, at_tail::out) is det.
+
+not_at_tail(yes(_), no(have_not_seen_reccall)).
+not_at_tail(no(SeenReccall), no(SeenReccall)).
% The `locals' type contains a list of local definitions
% which are in scope.
@@ -170,7 +195,7 @@ mark_tailcalls_in_defn(ModuleName, Defn0, Defn, Warnings) :-
AtTail = yes([])
;
RetTypes = [_ | _],
- AtTail = no
+ AtTail = no(have_not_seen_reccall)
),
TCallInfo = tailcall_info(ModuleName, Name, Locals),
mark_tailcalls_in_function_body(TCallInfo, AtTail, Warnings,
@@ -269,10 +294,11 @@ mark_tailcalls_in_stmt(TCallInfo, Context, Warnings, AtTailAfter,
% The statement in the body of a while loop is never in a tail
% position.
Stmt0 = ml_stmt_while(Kind, Rval, Statement0),
- mark_tailcalls_in_statement(TCallInfo, Warnings, no, _,
- Statement0, Statement),
+ seen_reccall(AtTailAfter, SeenReccallAfter),
+ mark_tailcalls_in_statement(TCallInfo, Warnings,
+ no(SeenReccallAfter), AtTailBefore0, Statement0, Statement),
% Neither is any statement before the loop.
- AtTailBefore = no,
+ not_at_tail(AtTailBefore0, AtTailBefore),
Stmt = ml_stmt_while(Kind, Rval, Statement)
;
% Both the `then' and the `else' parts of an if-then-else are in a
@@ -283,7 +309,9 @@ mark_tailcalls_in_stmt(TCallInfo, Context, Warnings, AtTailAfter,
mark_tailcalls_in_maybe_statement(TCallInfo, ElseWarnings,
AtTailAfter, _, MaybeElse0, MaybeElse),
Warnings = ThenWarnings ++ ElseWarnings,
- AtTailBefore = no,
+ % This is conservative, if improved it may remove some false
+ % positive tailcall warnings.
+ AtTailBefore = no(have_not_seen_reccall),
Stmt = ml_stmt_if_then_else(Cond, Then, MaybeElse)
;
% All of the cases of a switch (including the default) are in a
@@ -294,7 +322,9 @@ mark_tailcalls_in_stmt(TCallInfo, Context, Warnings, AtTailAfter,
mark_tailcalls_in_default(TCallInfo, AtTailAfter, DefaultWarnings,
Default0, Default),
Warnings = CasesWarnings ++ DefaultWarnings,
- AtTailBefore = no,
+ % This is conservative, if improved it may remove some false
+ % positive tailcall warnings.
+ AtTailBefore = no(have_not_seen_reccall),
Stmt = ml_stmt_switch(Type, Val, Range, Cases, Default)
;
Stmt0 = ml_stmt_call(_, _, _, _, _, _),
@@ -310,7 +340,7 @@ mark_tailcalls_in_stmt(TCallInfo, Context, Warnings, AtTailAfter,
mark_tailcalls_in_statement(TCallInfo, HandlerWarnings, AtTailAfter, _,
Handler0, Handler),
Warnings = TryWarnings ++ HandlerWarnings,
- AtTailBefore = no,
+ AtTailBefore = no(have_not_seen_reccall),
Stmt = ml_stmt_try_commit(Ref, Statement, Handler)
;
% XXX: Maybe not true for some of these.
@@ -321,7 +351,7 @@ mark_tailcalls_in_stmt(TCallInfo, Context, Warnings, AtTailAfter,
; Stmt0 = ml_stmt_atomic(_)
),
Warnings = [],
- AtTailBefore = no,
+ not_at_tail(AtTailAfter, AtTailBefore),
Stmt = Stmt0
;
Stmt0 = ml_stmt_return(ReturnVals),
@@ -371,24 +401,33 @@ mark_tailcalls_in_stmt_call(TCallInfo, Context, Warnings,
% Mark this call as a tail call.
Stmt = ml_stmt_call(Sig, Func, Obj, Args, ReturnLvals,
tail_call),
- Warnings = []
+ Warnings = [],
+ AtTailBefore = no(seen_reccall)
else
- Stmt = Stmt0,
+ seen_reccall(AtTailAfter, SeenReccall),
(
- CodeAddr = code_addr_proc(QualProcLabel, _Sig)
+ SeenReccall = seen_reccall,
+ Warnings = []
;
- CodeAddr = code_addr_internal(QualProcLabel, _SeqNum, _Sig)
+ SeenReccall = have_not_seen_reccall,
+ (
+ CodeAddr = code_addr_proc(QualProcLabel, _Sig)
+ ;
+ CodeAddr = code_addr_internal(QualProcLabel, _SeqNum, _Sig)
+ ),
+ QualProcLabel = qual(_, _, ProcLabel),
+ ProcLabel = mlds_proc_label(PredLabel, ProcId),
+ Warnings = [tailcall_warning(PredLabel, ProcId, Context)]
),
- QualProcLabel = qual(_, _, ProcLabel),
- ProcLabel = mlds_proc_label(PredLabel, ProcId),
- Warnings = [tailcall_warning(PredLabel, ProcId, Context)]
+ Stmt = Stmt0,
+ AtTailBefore = no(seen_reccall)
)
else
% Leave this call unchanged.
Stmt = Stmt0,
- Warnings = []
- ),
- AtTailBefore = no.
+ Warnings = [],
+ not_at_tail(AtTailAfter, AtTailBefore)
+ ).
:- pred mark_tailcalls_in_cases(tailcall_info::in, at_tail::in,
list(tailcall_warning)::out,
--
2.6.1
More information about the reviews
mailing list