Tk Source Code

View Ticket
Login
2025-05-19
10:22 Closed ticket [7231bf99]: Setting ttk state may change the variable passed by value plus 7 other changes artifact: 16ad3fbf user: jan.nijtmans
10:21
Fix [7231bf9941]: Setting ttk state may change the a variable passed by value check-in: 3e103b4d user: jan.nijtmans tags: trunk, main
10:20
Fix [7231bf9941]: Setting ttk state may change the a variable passed by value check-in: 9df91a3e user: jan.nijtmans tags: core-9-0-branch
09:46
Fix [7231bf9941]: Setting ttk state may change the a variable passed by value check-in: 151d1e44 user: jan.nijtmans tags: core-8-6-branch
2025-05-18
20:12 Ticket [7231bf99] Setting ttk state may change the variable passed by value status still Open with 3 other changes artifact: 7d8707ac user: jan.nijtmans
19:35
Fix [7231bf9941]: Setting ttk state may change the a variable passed by value Closed-Leaf check-in: afb43975 user: jan.nijtmans tags: bug-7231bf9941
2025-05-17
22:20 Open ticket [7231bf99]: Setting ttk state may change the variable passed by value plus 3 other changes artifact: 4f89e77b user: sbron
21:33 Ticket [7231bf99]: 3 changes artifact: 6329d0db user: chw
21:31 Pending ticket [7231bf99]. artifact: 59a6f517 user: chw
21:26 Ticket [7231bf99]: 4 changes artifact: 0865ab39 user: chw
21:15 Closed ticket [7231bf99]. artifact: 2d9b7374 user: chw
18:29 Ticket [7231bf99]: 3 changes artifact: 7d24f6c4 user: jan.nijtmans
16:53 Ticket [7231bf99]: 3 changes artifact: 1a7f8539 user: chw
15:13 Ticket [7231bf99]: 3 changes artifact: 1c7ba21a user: sbron
09:37 Ticket [7231bf99]: 3 changes artifact: 5b427fbb user: chw
09:15 Ticket [7231bf99]: 3 changes artifact: 1acce075 user: jan.nijtmans
08:41 Ticket [7231bf99]: 3 changes artifact: fc539bb1 user: chw
08:28 Ticket [7231bf99]: 3 changes artifact: b737a61e user: fvogel
08:24 Ticket [7231bf99]: 3 changes artifact: 97a6391d user: sbron
08:18 Ticket [7231bf99]: 3 changes artifact: 6b08ffc6 user: sbron
07:55 Ticket [7231bf99]: 3 changes artifact: e57968ca user: fvogel
07:38 Ticket [7231bf99]: 3 changes artifact: 19c94334 user: chw
2025-05-16
22:52 New ticket [7231bf99]. artifact: 8cbdb5f9 user: sbron

Ticket UUID: 7231bf99417b34597f61eb7f204744f83937302a
Title: Setting ttk state may change the variable passed by value
Type: Bug Version: 8.6.16
Submitter: sbron Created on: 2025-05-16 22:52:19
Subsystem: 88. Themed Tk Assigned To: jan.nijtmans
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2025-05-19 10:22:52
Resolution: Fixed Closed By: jan.nijtmans
    Closed on: 2025-05-19 10:22:52
Description:

My code wasn't working as I expected and I couldn't see what I was doing wrong. I finally found some strange behavior from ttk when I reduced the code to:

    ttk::button .b
    set state [list invalid disabled]
    puts [join $state]
    .b state $state
    puts [join $state]
The first puts prints "invalid disabled", the second prints the list states in reverse order; "disabled invalid". So the value of the 'state' variable has changed, even though it was passed by value. I don't think that is in line with how Tcl is supposed to work.

Using tcl::unsupported::representation I found out that the internal representation of the value is changed from a list to a StateSpec. A StateSpec is a set of bit flags. It can be converted back to a list, but it uses a fixed order for the states, which may differ from the original list.

Using join is important, because if you just puts $state the list stays in the original order. I presume that happens because that causes a string representation to be generated. This first threw me while trying to debug.

When I figured out the cause of the problem, I could work around it by doing a dummy string length $state. Or better, build the state variable with the states in the order ttk expects them.

While I found the problem on Tk 8.6.16, I checked that it is still reproducible on the current trunk.

User Comments: jan.nijtmans added on 2025-05-19 10:22:52:

Fixed [9df91a3ecd48bbc2|here] (and in the other branches too)


jan.nijtmans added on 2025-05-18 20:12:06:

Fix, with testcase, [afb43975a7cf9b2a|here]


sbron added on 2025-05-17 22:20:16:

Yes, the internal type of a value can change. If you run llength on a value with an internal type of dict, its internal type is changed to list. That's fine as long as the value doesn't change. Now please let's get back to the original problem of this ticket.


chw added on 2025-05-17 21:26:17:

Let me explain it:

  • Given variable X which has value V of internal type T
  • A widget gets the value V assigned
  • By the assignment operation V's internal type is change to U
  • At the end variable X has another type U

Is this expected outcome?


chw added on 2025-05-17 21:15:36:

Jan, let's see the fix. The point remains that the object type is changed (no immutability) which can cause interesting unexpected effects. The pixel stuff might well be fixed in 9.0 (is it really? cf. immutability) but how to prove that in any other object type implementations where similar problems may or may not exist? BTW: since when did we abandon 8.6?


jan.nijtmans added on 2025-05-17 18:29:54:

@Christian. I agree with Schelte that your example with "-border" had no relation with this ticket.

It's fixed in Tk9.0, and there is already a ticket for it: [29ba539501]. So, no need any more to discuss about it in this (unrelated) ticket.


chw added on 2025-05-17 16:53:15:

Schelte, my point is that similar to your problem description the object type of the variable is changed with unintended consequences on the further usage of the variable. This due to some kind of EIAS violation, isn't it?


sbron added on 2025-05-17 15:13:47:

Sorry Christian, I don't see the relevance to this ticket. Sure, the -border option returns something else than what you set it to (at least when run from a script, interactively it works fine). But the value of variable 'x' remains unharmed.


chw added on 2025-05-17 09:37:35:

Or make the conversion from the very start on on a duplicated object such that the original object remains unmodified.


jan.nijtmans added on 2025-05-17 09:15:39:

I think I understand where the problem is. When converting the list to a StateSpec, the list has no string rep, the resulting StateSpec neither. In this case we _must_ always generate the string representation of the list, otherwise we - indeed - violate EIAS: The string representation of a list is different from the string representation of a StateSpec. The conversion should never change the string representation


chw added on 2025-05-17 08:41:02:

Sorry Schelte, I've pasted the wrong code, try this one:

  tk scaling 1
  set x 10m
  . configure -border $x
  puts [. cget -border]
  puts $x
  tk scaling 2
  . configure -border $x
  puts [. cget -border]
  puts $x
  set x [string range X$x 1 end]
  . configure -border $x
  puts [. cget -border]
  puts $x
  # more fun
  set x [string repeat 1 50]
  . configure -border $x
  puts [. cget -border]
  puts $x
  set x [string repeat 1 500]
  . configure -border $x
  puts [. cget -border]
  puts $x

fvogel added on 2025-05-17 08:28:08:
Oh sorry, right, that was it (running the commands in interactive mode). Can repro now. Even 8.6.9 shows the issue.

sbron added on 2025-05-17 08:24:32:

Christian,

While the second error message is a bit unexpected, I don't see any changing of variables under the hood when running the commands you mentioned. That was the main point of my bug report.


sbron added on 2025-05-17 08:18:02:

Are you running the commands interactively? In that case, the REPL will print the result of set state [list invalid disabled], generating the string representation. That circumvents the problem.

To run interactively, use:

    ttk::button .b
    set state [list invalid disabled];list
    puts [join $state]
    .b state $state
    puts [join $state]


fvogel added on 2025-05-17 07:55:39:
Hmmm... I'm not reproducing what Schelte is reporting. This is on Windows, MSVC, debug build, with either the tip of the trunk branch, the tip of core-8-6-branch, or the core-8-6-16 tag. For me the two puts both spit "invalid disabled".

chw added on 2025-05-17 07:38:33:

Schelte,

I believe you've hit a nice little pothole while driving on the Tk road which is partially filled with a can of worms. You were on the Ttk lane, but it seems even more common, e.g.

  tk scaling 1
  set x 10
  . configure -border $x
  puts [. cget -border]
  tk scaling 2
  . configure -border $x
  puts [. cget -border]
  incr x 0
  . configure -border $x
  puts [. cget -border]
  # more fun
  set x [string repeat x 50]
  . configure -border $x
  puts [. cget -border]
  puts $x
  set x [string repeat x 500]
  . configure -border $x
  puts [. cget -border]
  puts $x

Could be that results differ between 8.6 and 9.0 (didn't test the latter).

Could even be that this requires further discussion regarding intentions, expectations, and consequences.