MS This page deals with deep Tcl internals. Not to be perused immediately after lunch. Very few readers will be interested.
Some recent conceptual difficulties with the proper behaviour for command invocations led me to a bit of archeological discovery in search for answers. Why does TCL_EVAL_INVOKE do what it does? Is it really the correct thing? What should it actually be doing?
With the help of 'cvs blame' I reconstructed this history. First the facts, then a section of comments and explanations on the special needs for the flag bits that control the behaviour of TclEvalObjvInternal: TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE.
This history looks at Tcl8.4, where it was mainly played. In Tcl8.5 namespace ensemble is a new user of TCL_EVAL_INVOKE. Up to 8.4
HISTORY
Note: revision numbers refer to the file tclBasic.c
1.65 (starting point)
1.66 (2002-07-29) In order to fix [Bug 582522] aliases do not fire exec traces [L1 ] the code for [interp alias] is modified to call through the main Tcl invoker: TclEvalObjvInternal. A new flag bit TCL_EVAL_NO_TRACEBACK is added so that AliasObjCmd keeps control of the generated error messages.
1.67 (2002-07-29) birth of TCL_EVAL_INVOKE in place of TCL_EVAL_NO_TRACEBACK, change in invocations:
1.75.2.1 (2003-04-25)
1.75.2.20 (2006-02-28) Fix for [Bug 1439836] TCL_EVAL_GLOBAL vs. [uplevel #0] [L2 ]
1.75.2.21 (2006-03-21) Fix for [Bug 1444291] [::unknown] and alias targets (TCL_EVAL_INVOKE) [L3 ]. TCL_EVAL_INVOKE ceases to set a #0 context; instead, it selectively changes the namespace of the current context to global.
COMMENTS
I think that TCL_EVAL_GLOBAL has essentially converged to its proper meaning.
In order to judge if this is the case for TCL_EVAL_INVOKE it is necessary to specify what that meaning is. The meaning is do whatever is needed for alias invocations, but that itself is not quite clear. It has at least two independent components:
The first goal is simple enough, the second is not. Aliases are supposed to lookup the command in global scope, but execute it in the caller's scope.
The simplest correct solution is possibly for the command name to be fully qualified at alias creation time, and to go back to a simple TCL_EVAL_NO_TRACEBACK flag for aliases.
There are several difficulties with the idea of resolving the command name in one namespace, then executing it in the caller's context. Essentially that info level breaks in one or the other of its different usages:
This leads me to think that it is impossible (at least, unadvisable) to consider different namespaces for resolution and execution until a decision is made to break one of these idioms - and provide an alternative. Maybe [info call] for "what will work correctly under [uplevel 1]".
Which in turn implies the infeasibility of my own TIPs 283/284, and a reduced utility for [namespace path] and [namespace unknown] (as their functionality is not triggered for FQ command names).
2006-11-4 I have just found out that idiom #1 is already broken since ... forever?
mig@ice:/tmp$ cat /tmp/test set res {} proc x {} { if {![llength $::res]} { set ::res [list [info level 0] :: [uplevel 1 [info level 0]]] } else { return :: } } namespace eval a { proc x {} {return ::a} } interp alias {} y {} x namespace eval a y puts $res mig@ice:/tmp$ /home/CVS/tcl8.3.4/unix/tclsh test x :: ::a
Tcl8.4 and 8.5 give the same result: in this configuration, [uplevel 1 [info level 0]]] fails to produce a second call to the same function. This a bug in the idiom that is fairly difficult to hit: the alias' target has to be shadowed by a command in the caller's context for it to fail.
Note that the situation is actually easily solvable for [interp alias]: it could just call the target with a FQ name, which would insure that it is looked up globally as specified. An TCL_EVAL_INVOKE would just be reduced to TCL_EVAL_NO_BACKTRACE again. The only change would be in stack traces.
In 8.5 that solution may not be optimal, as it would disable the new namespace path/unknown features for these calls.
Tip 283 would cause the bug in this idiom to be easier to hit as the real target command is generally not in the global namespace. A failure will occurs whenever the ensemble command is defined on a namespace other than ::, and it is called from any namespace other than its own.