Tcl Source Code

View Ticket
Login
2014-02-06
22:48
[a4494e28ed] Use flag bit instead of NULL pointer to suppress teardown list of importe... check-in: ec28d672c6 user: dgp tags: trunk
22:46 Closed ticket [a4494e28ed]: alias trace did not find command in real command list of import references plus 7 other changes artifact: dbdff2886c user: dgp
22:38
[a4494e28ed] Use flag bit instead of NULL pointer to suppress teardown list of imported commands whe... check-in: 3bf0700110 user: dgp tags: core-8-5-branch
22:34 Ticket [a4494e28ed] alias trace did not find command in real command list of import references status still Open with 3 other changes artifact: e1b9d44716 user: msofer
22:16 Ticket [a4494e28ed]: 3 changes artifact: f1c5fdd0bd user: dgp
16:09 Ticket [a4494e28ed]: 3 changes artifact: c1d9a9b083 user: dgp
16:01 Ticket [a4494e28ed]: 3 changes artifact: 21847aa808 user: dgp
15:50 Ticket [a4494e28ed]: 3 changes artifact: a3a31126c2 user: dgp
15:37 Ticket [a4494e28ed]: 3 changes artifact: c981c459eb user: dgp
15:15 Ticket [a4494e28ed]: 4 changes artifact: 053c66d7bc user: miguel
13:20 Ticket [a4494e28ed]: 3 changes artifact: 17ea9a0911 user: miguel
2014-02-05
23:21 Ticket [a4494e28ed]: 3 changes artifact: e5e34e0296 user: dgp
13:13 Ticket [a4494e28ed]: 7 changes artifact: c90e7e5ba8 user: dkf
03:11 New ticket [a4494e28ed]. artifact: bfa13115b7 user: pooryorick

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:

I can reproduce with 8.5 (8.5.9) as well.