Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0007525OCamllanguage featurespublic2017-05-04 15:502017-05-05 16:19
Assigned Togasche 
StatusresolvedResolutionno change required 
PlatformOSOS Version
Product Version4.03.0 
Target VersionFixed in Version 
Summary0007525: [@tailcall] attribute fails to warn about some stack calls
Descriptionexample code:

let () =
  let x f g =
    f ();
    g ();
    f ();
  let rec inner () =
    (inner [@tailcall]) (); (* compiler warns about this *)
      (fun () -> ())
      (fun () -> if false then (inner [@tailcall]) ()) (* but wrongly thinks this code is ok? *)
  inner ()


Encountered this problem when doing a recursive call in an exception handler (e.g. with Lwt.catch)
TagsNo tags attached.
Attached Files

- Relationships

-  Notes
gasche (administrator)
2017-05-04 19:33

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.
domsj (reporter)
2017-05-05 15:41

Let's use Lwt.catch as an example. see [^] 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?
gasche (administrator)
2017-05-05 16:19

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.

- Issue History
Date Modified Username Field Change
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
Powered by Mantis Bugtracker