Tcl Source Code

View Ticket
Login
Ticket UUID: 633204
Title: catch { return foo } breaks inside proc
Type: Bug Version: obsolete: 8.4.1
Submitter: nobody Created on: 2002-11-04 11:05:42
Subsystem: 47. Bytecode Compiler Assigned To: dgp
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2003-01-10 04:17:29
Resolution: Fixed Closed By: dgp
    Closed on: 2003-01-09 21:17:29
Description:
In TCL 8.4.1: 

% info patchlevel
8.4.1
% catch { return foo }
2
% proc foo {} { catch { return foo } }
% foo
0
%

???  Putting the code inside a proc changes the return value?
These should return the same thing.  In Tcl 8.3.3 

% info patchlevel
8.3.3
% catch { return foo }
2
% proc foo {} { catch { return foo } }
% foo
2
%

Since the behaviour changes when the code is put inside a proc, 
I *suspect* a bytecode compiler bug.

Details: 

Tcl 8.4.1
$ uname -a
SunOS xxxxxxxx 5.7 Generic_106541-10 sun4u sparc 
SUNW,UltraSPARC-IIi-cEngine
$ gcc --version
2.95.2
User Comments: dgp added on 2003-01-10 04:17:29:
Logged In: YES 
user_id=80530

correction patch committed to HEAD.

dgp added on 2003-01-10 04:11:13:

File Added - 39299: correction.patch

Logged In: YES 
user_id=80530

Ack!  workaround.patch had an "off-by-one" error.

Here's a correction.patch to fix things.

dgp added on 2003-01-08 07:37:04:
Logged In: YES 
user_id=80530

workaround patch committed.

dgp added on 2002-12-19 06:56:14:

File Added - 37922: workaround.patch

Logged In: YES 
user_id=80530

Here's a patch that implements
the workaround suitable for 8.4.x

dgp added on 2002-12-19 05:42:38:
Logged In: YES 
user_id=80530

The bugs I feared in calls to
Tcl_EvalObjEx() are probably 
prevented by lines 2401-2403
of tclCompCmds.s where
the bytecompiling of [return]
is disabled if an enclosing proc
is not detected.

This is similar in spirit to the proposed
workaround for this bug that
involves disabling bytecompiling
when an enclosing [catch] is
present.

dgp added on 2002-12-19 00:42:10:
Logged In: YES 
user_id=80530

The patch defines a new opcode in
the bytecode execution engine.
In order to maintain compatibility
with external bytecode brought in
by tbcload, new opcodes can only
be defined for a new minor release
of Tcl, so this patch can't be applied
until 8.5 development.

So,
1. Can't add an opcode.
2. No existing opcode returns TCL_RETURN,
Therefore,
the only correct fix that can be applied in the
8.4.x timeframe is to disable bytecompiling
of [return].

A workaround that would still be buggy, but
would correct the reported example script,
would be to add special detection of an
enclosing [catch] in TclCompileReturnCmd()
and return TCL_OUT_LINE_COMPILE in
that case.

The remaining bug after that workaround
would be harder to demo.  TEBC and
TclCompEvalObj() should evaluate general
Tcl scripts.  The bug is that they are
implicitly converting TCL_RETURN to TCL_OK,
as if they were only good for evaluating proc
bodies.  This bug might still be exposed by
C code that calls Tcl_EvalObjEx().

Better to get to 8.5 development and get
the correct fix in the BC engine with the
new opcode.

dgp added on 2002-12-18 06:15:35:

File Deleted - 37850: 



File Added - 37851: return.patch

dgp added on 2002-12-18 06:15:34:
Logged In: YES 
user_id=80530

Improved patch; same basic approach.

dgp added on 2002-12-18 06:07:30:

File Added - 37850: return.patch

Logged In: YES 
user_id=80530

Attached is a patch that appears to
be an effective fix.

I don't fully know what I'm doing 
in the BC engine, so it's very
likely not the best fix, but it appears
to fix the bug, and test suite results
are unchanged.

msofer added on 2002-12-17 02:47:30:
Logged In: YES 
user_id=148712

When compiling the body of 'a', the compiler encounters a
bcc'ed command 'catch and proceeds to compile its arguments;
as they are also bcc'ed ('return'), the whole thing gets
compiled into the body of 'a'.

When compiling 'b', the compiler encounters a non-bcc'ed
command 'myreturn', and compiles an invocation for it.

The effect at runtime is: the 'return' of 'a' executes in
the same TEBC instance as the proc body, there is no
C-return involved. OTOH, the return of 'b' comes from a
child instance of TEBC, via a real C-return.

The internal return in the first case cannot be handled in
the same manner: as it essentially executes in the same
process, the return value of an inlined 'retun -code $x'
*has* to be $x for (almost) everything to work as it should
- the exception being what we're talking about here :(

I imagine things could conceivably be repaired, but at a
high complexity cost in the compiler: the return-compiler
could check if it is compiling within a catch-range or not,
and compile-in the correct return code: TCL_OK if it is not
caught, TCL_RETURN otherwise. New idea, I'll think about
that ...

dgp added on 2002-12-17 01:52:36:
Logged In: YES 
user_id=80530

Yes, but both the body of [a]
and the body of [b] are bytecompiled, right?

So the problem is that
bytecompiled [return] is
not returning TCL_RETURN, when
it should be, just like "bytecompiled"
[myreturn] does.

The example just shows that the TEBC
and TclObjInterpProc framwork can
handle a TCL_RETURN properly, so
what's the obstacle to fixing BC'd [return]
to return the correct return code?

hobbs added on 2002-12-17 01:36:46:
Logged In: YES 
user_id=72656

In 'myreturn', the return is not byte-compiled, but a 
regular "return" would be.

dgp added on 2002-12-17 00:47:05:
Logged In: YES 
user_id=80530

The BC execution engine appears
to be capable of doing the right thing:

% proc myreturn {} {return -code return}
% proc a {} {catch return}
% proc b {} {catch myreturn}
% a
0
% b
2

So what path through TEBC does
[a] take, and how can TEBC be
corrected so that [b] takes a similar
path?

At least this seems to confirm that
TEBC is capable of returning TCL_RETURN,
and if TEBC does return TCL_RETURN,
then TclObjInterpProc() will do the right thing.

dgp added on 2002-12-13 01:20:23:
Logged In: YES 
user_id=80530

Note an apparent workaround is
to defeat the compiling of
[return] like so:

proc foo {} {catch {return -code ok foo}}

dgp added on 2002-12-13 01:13:18:
Logged In: YES 
user_id=80530

No, think I misread things.
Real problem doesn't appear
to be that simple after all.

dgp added on 2002-12-12 23:20:18:
Logged In: YES 
user_id=80530

seems this bug has been lurking
in there for years, but only the
recent bytecompilation of [return]
has made it easy to expose?

dgp added on 2002-12-12 23:13:52:
Logged In: YES 
user_id=80530

Is this bug found in line 343 of
tclCompCmds.c ?  The
comment on lines 329-333 says:

    /*
     * The "no errors" epilogue code: store the body's
result into the
     * variable (if any), push "0" (TCL_OK) as the catch's
"no error"
     * result, and jump around the "error case" code.
     */

but the result of [catch] should not always be TCL_OK.
It should be whatever the return code of the $script
argument was.

dgp added on 2002-12-12 05:38:59:
Logged In: YES 
user_id=80530

This must be a bug in the
byte-compiling of [catch].

Compare with:

% proc foo {} {
set catch catch
$catch {return foo}
}
% foo
2

and

% proc bar {} {
set script {return foo}
catch $script
}
% bar
2

Only the literal "catch {return foo}"
is causing trouble, indicating some
mistaken "optimization" in the
bytecode.

msofer added on 2002-11-05 07:19:16:
Logged In: YES 
user_id=148712

Blast, bitten by bothersome bytecode bug! I do not
immediately see how to fix this one :(

Attachments: