Tcl Source Code

View Ticket
Login
Ticket UUID: ecf35c71200d782cbd63e15c4df80b5e3c75cbd4
Title: [return]: broken ordering of nested -options
Type: Bug Version: 8.5+
Submitter: dgp Created on: 2025-06-20 15:33:36
Subsystem: 45. Parsing and Eval Assigned To: dgp
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2025-06-24 16:24:36
Resolution: Fixed Closed By: dgp
    Closed on: 2025-06-24 16:24:36
Description:
While out on an arduous hunt for a memory leak, I noticed that
the coding of TclMergeReturnOptions() contains an ordering error
which can be demonstrated in an interactive session:

% catch {return -level 0 -options {-options {-code break} -code ok}} m o
3
% set o
-code 3 -level 0
User Comments: dgp added on 2025-06-24 16:24:36:
Fix accepted into the 9.0 branch

dgp added on 2025-06-23 19:59:06:
A pending fix is committed to the bugfix branch.

dgp added on 2025-06-23 17:42:17:
Branch bug-ecf35c7120 is open to address this ticket.  So far it includes
tests demonstrating the problem.  Briefly, processing so that the -options
switch emulates {*} argument expansion is not consistent with standard
dictionary creation processing in the handling of duplicate keys.

dgp added on 2025-06-20 15:50:24:
Note that only nesting brings out the trouble.
A single level of -options behaves properly

% catch {return -level 0 -options {-code break} -code ok} m o
0
% set o
-code 0 -level 0

dgp added on 2025-06-20 15:46:49:
The documentation says:

       -options options
              The  value  options must be a valid dictionary.  The entries of
              that dictionary are treated as additional  option  value  pairs
              for the return command.

While an explicit ordering guarantee is not stated, the description
suggests a lifting of the dictionary entries out of the dictionary
to enter the options alongside the other options.

This is the same kind of expansion offered by the {*} syntax, but
the -options option debuted at the same time as that syntax, when
reliance on it was unclear.

An explicit aim was to enable a re-raising pattern:

    catch $script m o
    return -options $o $m

Had the {*} syntax been better established at the time, we might
have gone without -options and prescribed:

    catch $script m 0
    return {*}$o $m

Whatever precise mental model is in use, we expect later appearances
of the same option to overwrite earlier ones, so that we reason:
 
    return -level 0 -options {-options {-code break} -code ok}

is same as

    return -level 0 -options {-code break} -code ok

is same as

    return -level 0 -code break -code ok

is same as

    return -level 0 -code ok

and that's not what Tcl is doing.

Nested options are rare.  A single test even attempts it
(cmdMZ-return-2.14), but doesn't illustrate the bug.
Not noticed for all this time.