Open richardleach opened 4 months ago
The difference first traces back to _Perl_opscope. Within that function, dumping OP *o
for the true case block shows OPf_PARENS
set on the lineseq OP:
1 lineseq LISTOP(0x5564808c46c0) ===> [0x0]
PARENT ===> [0x0]
FLAGS = (UNKNOWN,KIDS,PARENS,SLABBED)
|
2 +--nextstate COP(0x5564808c4700) ===> [SELF]
| FLAGS = (VOID,SLABBED,MORESIB)
| LINE = 1
| PACKAGE = "main"
| SEQ = 4294967252
|
3 +--return LISTOP(0x5564808c4760) ===> [0x0]
FLAGS = (UNKNOWN,KIDS,SLABBED)
|
4 +--pushmark OP(0x5564808c47a0) ===> [SELF]
| FLAGS = (SCALAR,SLABBED,MORESIB)
|
5 +--const SVOP(0x5564808c47d0) ===> [SELF]
FLAGS = (SCALAR,SLABBED)
SV = IV(0)
For the else block, OPf_PARENS
is not set on the lineseq OP:
6 lineseq LISTOP(0x5564808c4808) ===> [0x0]
PARENT ===> [0x0]
FLAGS = (UNKNOWN,KIDS,SLABBED)
|
7 +--nextstate COP(0x5564808c4848) ===> [SELF]
| FLAGS = (VOID,SLABBED,MORESIB)
| LINE = 1
| PACKAGE = "main"
| SEQ = 4294967250
|
8 +--return LISTOP(0x5564808c48a8) ===> [0x0]
FLAGS = (UNKNOWN,KIDS,SLABBED)
|
9 +--pushmark OP(0x5564808c48e8) ===> [SELF]
| FLAGS = (SCALAR,SLABBED,MORESIB)
|
10 +--const SVOP(0x5564808c4918) ===> [SELF]
FLAGS = (SCALAR,SLABBED)
SV = IV(1)
The presence or absence of OPf_PARENS
here controls whether o
is wrapped in enter
/leave
, or the lineseq
OP is converted into a scope
OP.
The callers to _Perl_opscope are:
Perl_yyparse (gramtype=gramtype@entry=258) at /home/rich/github/perl5/perly.y:702
/* else and elsif blocks */
else
: empty
| KW_ELSE mblock
{
($mblock)->op_flags |= OPf_PARENS;
$$ = op_scope($mblock);
}
Perl_yyparse (gramtype=gramtype@entry=258) at /home/rich/github/perl5/perly.y:466
| KW_IF PERLY_PAREN_OPEN remember mexpr PERLY_PAREN_CLOSE mblock else
{
$$ = block_end($remember,
newCONDOP(0, $mexpr, op_scope($mblock), $else));
parser->copline = (line_t)$KW_IF;
}
I have no understanding of the parser logic and don't know why ($mblock)->op_flags |= OPf_PARENS;
isn't present in both locations.
Here's a simple example demonstrating this:
package foo;
sub new {
return bless {}, 'foo';
}
sub DESTROY {
$main::status = "Finished";
}
package main;
for (0..1) {
$status = "Started";
print $status, "\n";
if ($_) {
my $magic = foo->new();
} else {
my $magic = foo->new();
}
print " $status\n";
}
print "$status\n";
A developer reasoning about this Perl code might expect the output to be:
Started
Finished
Started
Finished
Finished
whereas it actually outputs this:
Started
Finished
Started
Started
Finished
because the destructor isn't called at the expected time when the true branch of the if statement is followed.
Description Given an if/else branch, such as the example below, it seems like a reasonable expectation that both branches behave identically with regard to entering a new scope - or not doing so.
But they don't. The else branch is wrapped in an ENTER/LEAVE pair, but the if branch is not.
That's not always the behaviour. In the following example, both branches are wrapped in an ENTER/LEAVE pair:
The inconsistency seems undesirable because:
Steps to Reproduce
Expected behavior Both branches have consistent scope behaviour.
Perl configuration Standard blead, v5.36