Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0007856OCamllanguage featurespublic2018-09-27 12:382018-09-28 00:01
Assigned To 
Platformx86-64OSUbuntuOS Version18.04
Product Version4.07.0 
Target VersionFixed in Version 
Summary0007856: @tailcall annotation doesn't show warning for non-tailcall
DescriptionWhen calling loop inside List.iter the compiler doesn't recognize that this call is not a tailcall, even when annotated with the @tailcall attribute.
Steps To Reproducecat > <<EOF
let rec loop () = List.iter (fun _ -> (loop[@tailcall]) ()) [1; 2];;
let rec loop2 () = try (loop2[@tailcall]) () with _ -> ();
ocamlc -w A

Actual output:
File "", line 2, characters 23-44:
Warning 51: expected tailcall

Expected output:
File "", line 1, characters ...:
Warning 51: expected tailcall
File "", line 2, characters 23-44:
Warning 51: expected tailcall
Additional InformationReproduced on OCaml 4.06.1 and OCaml 4.07.0

For a real life use case where this would've been useful see this pull request where we tried to add @tailcall to detect issues like this in the future but it didn't work: [^]
TagsNo tags attached.
Attached Files

- Relationships

-  Notes
nojebar (developer)
2018-09-27 12:46

I think current behavior is correct: in the first case loop is indeed in tail position: it is (fun _ -> ...) which is not.
edwin (reporter)
2018-09-27 14:29
edited on: 2018-09-27 14:30

Fair enough, what we actually wanted to know here is: is this recursive call guaranteed to not consume more stack space (including stack space already consumed by its immediate callers)?

Would be nice if there was another attribute that checked the tailcall property of all callers up to the recursive function itself: that might've helped catch bugs like this (we could manually add those annotations, but it would be error-prone when the code around it changes). This might not be easy to do, but if such an attribute is added it could err on the side of caution: if it cannot prove that the call doesn't consume more stack space, then it warns.

I think this would even work for recursive calls in Lwt, e.g.:
let rec foo =
   some_action () >>= fun v ->
   loop v

which would be equivalent to:
let rec foo =
   (>>=) (some_action ()) (fun v -> (loop [@recnostack]) v)

(fun v -> (loop[@recnostack]) v) --> OK, tail position, adds [@recnostack] annotation to (>>=) internally
(>>=) ... ... ---> OK tail position
let rec foo = ... -> OK we reached the function itself

(Of course you can still have an infinite loop that doesn't overflow the stack, but that is outside of scope for a compiler warning)

What do you think?

- Issue History
Date Modified Username Field Change
2018-09-27 12:38 edwin New Issue
2018-09-27 12:46 nojebar Note Added: 0019389
2018-09-27 14:29 edwin Note Added: 0019390
2018-09-27 14:30 edwin Note Edited: 0019390 View Revisions

Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker