Tcl Source Code

View Ticket
Login
Ticket UUID: a4494e28edfaa3a5d064dd1d91cfbd6163b6fb20
Title: alias trace did not find command in real command list of import references
Type: Bug Version: 8.6.1
Submitter: pooryorick Created on: 2014-02-05 03:11:33
Subsystem: 45. Parsing and Eval Assigned To: dgp
Priority: 9 Immediate Severity: Severe
Status: Closed Last Modified: 2014-02-06 22:46:52
Resolution: Fixed Closed By: dgp
    Closed on: 2014-02-06 22:46:52
Description:
In Tcl 8.6.1, the code below produces the following message, and the interpreter aborts:

DeleteImportedCmd: did not find cmd in real cmd's list of import references


### begin code ###
#! /bin/env tclsh

proc alias {alias target} {
    set fulltarget [uplevel [list namespace which $target]]
    namespace eval [namespace qualifiers $fulltarget] \
        [list namespace export [namespace tail $fulltarget]]
    set newcmd [namespace eval [info cmdcount] [string map [
        list @{alias} [list $alias] @{fulltarget} [list $fulltarget] ] {
        namespace import @{fulltarget}
        rename [namespace tail @{fulltarget}]  @{alias}
        ::namespace export @{alias}
        ::namespace which @{alias}
    }]]
    uplevel [list namespace import $newcmd]
    uplevel [list trace add command $fulltarget delete [list apply [list {stash args} {
        namespace delete [namespace qualifiers $stash]
    }] $newcmd]]
}

proc proc1 args {}
alias newproc proc1
proc proc1 {} {}
User Comments: dgp added on 2014-02-06 22:46:52:
Fixed on active branches.

msofer added on 2014-02-06 22:34:50:
... but this margin is too small?

dgp added on 2014-02-06 22:16:51:
While exploring the clever stupid solution, I landed
on the boring effective solution instead.

dgp added on 2014-02-06 16:09:21:
dgp	I think I see a clever fix
dgp	there's a fine line between clever and stupid

dgp added on 2014-02-06 16:01:40:
miguel is correct that the panic is rooted in the
failure to cleanly define just what a [namespace import]
is really importing and the bizarre DWIM rules that
have grown up in the absence of that clean spec.

You can sensibly define import by name, or import by token,
but existing [namespace import] never made up its mind.

dgp added on 2014-02-06 15:50:06:
The "Don't Do That" workaround is to place
the command delete trace on the import, and
not the original.

namespace eval ::demo {
    proc foo args {}
    namespace export foo
    namespace eval ::x [list namespace import [namespace which foo]]
#    trace add command foo delete {namespace delete ::x;#}
    namespace eval ::x {trace add command foo delete {namespace delete ::x;#}}
    proc foo {} {}
}

dgp added on 2014-02-06 15:37:07:
Simplified demo:

namespace eval ::demo {
    proc foo args {}
    namespace export foo
    namespace eval ::x [list namespace import [namespace which foo]]
    trace add command foo delete {namespace delete ::x;#}
    proc foo {} {}
}

miguel added on 2014-02-06 15:15:33:
This is pretty involved - the required mechanics for deleting an imported command ALSO from a delete trace on the target are not in place, and it doesn't look trivial to implement. The trouble is (refs from checkout4aa075565662, today's top of core-8-5-branch):
  1. when the target is replaced via Tcl_CreateObjCommand, the importRefPtr is saved and set to NULL (tclBasic.c line 1891)
  2. the target is deleted, it's delete traces run causing the imported command to be deleted
  3. the imported command looks for a ref to itself in the target's importRefPtr, panics when it doesn't find itself

I do not see how to fix this. It is a conseuqence of what I consider to be a misfeature. Redefining a command maintains the exports - so that imported commands are NOT a link to the original implementation (what I would propose is the correct thing), but rather to the current implementation ... ASSUMING it wasn't EXPLICITLY deleted and then recreated, in which case the import ceases to exist. A weird bastard between an interp-alias and (what I believe should be) an import.

IOW, redefining a command is different from deleting/recreating it in terms of exports - but delete traces have no way to know about this difference. Maybe the solution should be that delete traces not be run when the command is being recreated?

miguel added on 2014-02-06 13:20:50:
Still trying to find out WTF. In the meantime, in the "don't do that" tradition, a simpler [alias] that does not have this problem - by deleting the temporary namespace tmpns immediately instead of setting a delete trace. I would actually consider having a permanent namespace for use as scratchpad - mimicking the /tmp directory.

proc alias {alias target} {
    set fulltarget [uplevel 1 [list namespace which $target]]
    set tailtarget [namespace tail $fulltarget]
    set tmpns [info cmdcount]
    namespace eval [namespace qualifiers $fulltarget] \
        [list namespace export $tailtarget]
    namespace eval $tmpns \
        [list namespace import $fulltarget]
    uplevel 1 [list rename \
        [namespace which ${tmpns}::$tailtarget] \
        $alias]
    namespace delete $tmpns
}

dgp added on 2014-02-05 23:21:53:
With the obvious conversion replacing [apply] with
another helper proc, the same trouble is in Tcl 8.4(.8)

dkf added on 2014-02-05 13:13:23: (text/html)
I can reproduce with 8.5 (8.5.9) as well.