Tcl Source Code

View Ticket
Login
Ticket UUID: 3048771
Title: tailcall constraints chafe with control command procs
Type: Bug Version: None
Submitter: dgp Created on: 2010-08-19 16:20:31
Subsystem: 45. Parsing and Eval Assigned To: msofer
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2010-08-30 21:08:29
Resolution: Fixed Closed By: msofer
    Closed on: 2010-08-30 14:08:29
Description:
A celebrated feature of Tcl is the ability to replace
built-in commands with functionally equivalent procs.
The constraints on [tailcall] diminish that ability.

Utility command from TIP 90:

% namespace eval control {
     proc ascaller script {
         if {[info level] < 2} {
             return -code error \
                     "[lindex [info level 0] 0] called outside a proc"
         }
         variable result
         variable options
         set code [uplevel 2 \
                 [list ::catch $script   [namespace which -variable result] \
                                         [namespace which -variable options]]]
         if {$code == 0} {
             return $result
         }
         dict incr options -level 2
         return -options $options $result
     }
 }
% proc myIf {cond body} {
    set bool [control::ascaller [list ::expr $cond]]
    if {!$bool} return
    return [control::ascaller $body]
}
% proc good {} {if 1 {tailcall concat}}
% good
% proc bad {} {myIf 1 {tailcall concat}}
% bad
tailcall can only be called from a proc or lambda
User Comments: msofer added on 2010-08-30 21:08:29:

allow_comments - 1

Patch committed; mores docs and tests needed: see new ticket #3056056

dgp added on 2010-08-24 20:51:42:
One place where this implementation may
cause surprise is with commands like [subst]
that have their own treatment of TCL_RETURN.

proc demo {template} {
    ...
    set result [subst $template]
    ....
    return $result
}
demo {[tailcall puts foo]}

The [subst] will effectively [catch] the TCL_RETURN.
The rest of [demo] will run, and then, possibly long
after the [subst], when [demo] returns, its results will
get replaced by the results of running [puts foo].

Similar issue is possible with [source].

ferrieux added on 2010-08-22 05:31:57:
Note that in such cases the order of execution is sometimes funny, tailcall scheduling something for execution at proc exit, but still disrupting the current sequence within the catch range... (That's just a note, OK once acknowledged)

Also note that an alternative would be 'eventually', that does only the scheduling-at-exit part, not the error code:

   proc f {} {
      puts AAA
      eventually g
      puts BBB
   }

so that the current tailcall would be [eventually ...;return]

dgp added on 2010-08-22 00:13:18:

File Added - 384146: 3048771.patch

dgp added on 2010-08-22 00:12:58:
Also broken, though less severely.
Uploading a working patch.

msofer added on 2010-08-21 21:12:53:

File Added - 384134: newTailcall.patch

msofer added on 2010-08-21 21:12:30:

File Deleted - 384089:

msofer added on 2010-08-21 21:12:09:
Those patches did not apply - sorry about that. Removed them, new one should be ok.

msofer added on 2010-08-21 04:21:06:

File Added - 384089: newTailcall2.patch

msofer added on 2010-08-21 04:20:23:
Better patch, fixes tailcall/coroutine interaction. Different from HEAD, but HEAD was arguably wrong: tailcall-13.1 fails.

msofer added on 2010-08-20 07:28:02:

File Deleted - 383990:

msofer added on 2010-08-20 07:27:49:

File Added - 384007: newTailcall.patch

msofer added on 2010-08-20 02:45:00:

File Added - 383990: diffH

msofer added on 2010-08-20 02:44:23:
Attached a patch that implements tailcall as returning TCL_RETURN, and where this works ok.

Changes in behaviour:
  - catch stops tailcall, but is not an error. The tailcall is scheduled anyway, and hence it will run unless it is cleared
  - tailcall with no arguments just clears any tailcall that might have been scheduled
  - tailcall replaces any previously scheduled tailcall with the new one

dgp added on 2010-08-19 23:55:22:
Still exploring the space, but currently my
preferences is to reduce the amount of
new "magic" in the implementation of
[tailcall] and let it use return codes like
any other command (and interact with
[catch] the same way as any other command).
Not yet clear whether it needs a new code,
or whether TCL_RETURN will do.

Miguel has suggested in the chat room that
[tailcall] might be revised to become the
(near- ?) equivalent of [atProcExit....; return].
Such a revision ought to play well with [catch], 
[uplevel], etc.  But all the implications are not
yet laid out.

ferrieux added on 2010-08-19 23:49:24:
Possibilities:
 (1) lose trust in [catch].
 (2) silently turn [tailcall] into a normal call when crossing a catch boundary, putting a limit on iterations (stack full).
 (3) automagically detect [catch] when it is at the end of a proc, and allow tailcall to cross it in that case.
Preferences ?

Attachments: