|Anonymous | Login | Signup for a new account||2017-11-24 15:47 CET|
|Main | My View | View Issues | Change Log | Roadmap|
|View Issue Details|
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0007525||OCaml||language features||public||2017-05-04 15:50||2017-05-05 16:19|
|Status||resolved||Resolution||no change required|
|Target Version||Fixed in Version|
|Summary||0007525: [@tailcall] attribute fails to warn about some stack calls|
let () =
let x f g =
let rec inner () =
(inner [@tailcall]) (); (* compiler warns about this *)
(fun () -> ())
(fun () -> if false then (inner [@tailcall]) ()) (* but wrongly thinks this code is ok? *)
Encountered this problem when doing a recursive call in an exception handler (e.g. with Lwt.catch)
|Tags||No tags attached.|
|It is not clear to me why you think there is a bug. Within the closure, the `inner [@tailcall] ()` call is a tail call. The closure itself is not called in tail position within `x`, so if you annotated the `g ();` call there you would get a warning.|
Let's use Lwt.catch as an example. see https://github.com/ocsigen/lwt/blob/3.0.0/src/core/lwt.ml#L702 [^] for the implementation.
The exception handler (f in the definition there) is not called in tail position, which is perfectly fine.
However when writing a recursive function which uses Lwt.catch I would like ensure that my function only calls itself in a tail recursive way.
I assumed that annotating all recursive calls with [@tailcall] would help me in doing this. And it does so partially, but apparently not when the recursive call happens from within a closure, then the attribute can't give me guarantees about how the function as a whole behaves.
(Even better than annotating all call sites I would like to annotate the function definition with [@tailrec]...)
I suppose there's no attribute to let the compiler help me enforce what I would like to achieve here?
The tailcall annotation has a simple, local semantics: it is applied on a call, and results in a warning if the call is not a tailcall.
What you are asking for is different, and there is no simple/modular way to interpret it: as long as a function is passed to a callee instead of called directly -- it is passed as a value, or used in a closure that it itself passed as a value -- you need to reason on the code of that callee to know if it will call its argument in tail-call position, or you need to strengthen the type-system to have a way to track this information across boundaries.
The simplest way to give the guarantee that you want would be to just warn on any use of the recursive function that is not a call in the current expression. But that this extremely restrictive, for example you could not use the @@ or |> operators on a recursive call, of kprintf, etc.
A more expressive way would be to distinguish a type of functions that may only be called in tail position (could be "(a -> b)[@tail]" for example), have the functions above enriched with these richer type annotations, and have something that would warn whether a recursive function is passed as an argument at a type that is not a tail-function type. But this sounds delicate to implement, requires subtyping, breaks principality, and does not interact very well with polymorphism.
|2017-05-04 15:50||domsj||New Issue|
|2017-05-04 19:33||gasche||Note Added: 0017767|
|2017-05-04 19:34||gasche||Status||new => feedback|
|2017-05-05 15:41||domsj||Note Added: 0017770|
|2017-05-05 15:41||domsj||Status||feedback => new|
|2017-05-05 16:19||gasche||Note Added: 0017771|
|2017-05-05 16:19||gasche||Status||new => resolved|
|2017-05-05 16:19||gasche||Resolution||open => no change required|
|2017-05-05 16:19||gasche||Assigned To||=> gasche|
|Copyright © 2000 - 2011 MantisBT Group|