Tcl DISCUSSION

Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

Started by edil Tcl variable scopeglobal commandupvarnamespacesTcl arrays in procedures
4 replies 248 views 5 participants
Latest activity · 30 Sep 2026

Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

edil Tcl Forum
#1

My build script starts with set top cpu_core and later defines proc report_top {} { puts "Top module: $top" }. Calling the procedure fails with can't read "top": no such variable. A second procedure that does set error_count 1 runs without complaint but the script-level error_count stays at 0. I also could not pass an array to a procedure as an argument.

How does variable scope work in Tcl procedures?

Community replies 4

Re: Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

#2

Every call of a procedure gets a fresh, empty set of local variables, containing only its arguments. Unlike C or Python, a procedure does not see script-level (global) variables automatically, not even for reading. That is the first error. The second symptom is the same rule seen from the other side: set error_count 1 inside the procedure creates a local variable that disappears on return, and the global one is never touched.

To use a global, either declare it inside the procedure with global top before using $top, or refer to it by its full name: $::top and set ::error_count 1.

Re: Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

#3

Globals are convenient in a 50-line script and a liability in a large flow where several sourced files may reuse names like top or i. Prefer passing values in and returning results: proc report_top {top} { ... }. Arguments can have defaults, proc build {top {jobs 4}} { ... }, so build cpu_core runs with 4 jobs and build cpu_core 8 with 8.

For settings that really are shared, put them in a namespace: namespace eval cfg { variable top cpu_core }. Procedures reach it as $::cfg::top, and nothing outside collides with it.

Re: Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

#4

Arrays need special handling because a Tcl array is a collection of variables, not a value, so $arr cannot be passed. Pass the array's name and link to it with upvar: proc bump {arrName key} { upvar 1 $arrName a; incr a($key) }, called as bump counts errors with no dollar sign. upvar 1 makes the local name a an alias for the variable of that name in the caller's scope, so the change is visible to the caller.

The same technique gives you output parameters for ordinary variables. If you only need to hand data over by value, use a dict (Tcl 8.5 and later), which is a real value, or convert with array get and array set.

Re: Tcl proc cannot read a variable set at the top of the script: how do global and upvar work?

#5

Callbacks are the other place where scope surprises people. Scripts registered with after, fileevent, trace or a tool's event hooks run later at global level, not inside the procedure that registered them, so a local variable named in the script no longer exists by then. Substitute the value when you create the callback, after 500 [list log_msg $text], or keep the state in a global or namespace variable.

For debugging, info exists name tells you whether a variable is visible at the current point, info vars lists the visible names and info level gives the current call depth (0 at script level).

TEP COMMUNITY