Tcl Source Code

View Ticket
Login
Ticket UUID: 477e96c250ba11df6cd4e8f548a0d06da078b0ed
Title: classvariable inside classmethod creates unusable variable
Type: Bug Created on: 2026-06-04 02:23:15
Submitter: anonymous Assigned to: nobody
Subsystem: 35. TclOO Package Severity: Minor
Priority: 5 Medium Last modified: 2026-06-04 02:23:15
Status: Open Closed by: nobody
Resolution: None Closed on:
Version: 9.1a0
Description:
In the following example, the classvariable m_columns statement inside of classmethod setit creates a variable that is unusable in the ordinary way in a new namespace in the ::oo space rather than link to the class-wide variable as one would expect. This produces a bug which is hard to diagnose.

Note that if one does the same thing in the initialize section, an error is thrown:  "self may only be called from inside a method". That error seems to be based on the imlementation of initialize and could probably be more helpful and direct with different wording, but at least it doesn't silently create an unreachable variable.

The workaround is to use variable instead of classvariable inside the classmethod. But this is likely to be forgotten since it doesn't seem consistent.

 oo::class create test1 {
    initialize {
        variable m_columns [dict create initial {value1 value2 value3}]
    }
    
    classmethod setit {} {
        classvariable m_columns
        dict set m_columns foo {garp parp snarp}
        puts [format {in setit: self=%s m_columns=%s} [self] $m_columns]
    }
    
    constructor {} {
        classvariable m_columns
        puts [format {in constructor: self=%s m_columns=%s} [self] $m_columns]
    }

    method showit {} {
        classvariable m_columns
        puts [format {in showit: self=%s m_columns=%s} [self] $m_columns]
    }
}


% test1 setit
in setit: self=::test1 m_columns=foo {garp parp snarp}
 % info object namespace ::test1
::oo::Obj106
 % set r [test1 new]
in constructor: self=::oo::Obj108 m_columns=initial {value1 value2 value3}
::oo::Obj108
 % $r showit
in showit: self=::oo::Obj108 m_columns=initial {value1 value2 value3}
 % 
 % info vars ::oo::Obj106::*
::oo::Obj106::m_columns
 % info vars ::oo::Obj107::*
::oo::Obj107::m_columns
 % info vars ::oo::Obj108::*
 % 
 % set ::oo::Obj106::m_columns
initial {value1 value2 value3}
 % set ::oo::Obj107::m_columns
foo {garp parp snarp}
 %