Apply HINT_BLOCK_SCOPE to lexical variable declarations #23867
+370
−394
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Apply HINT_BLOCK_SCOPE to lexical variable declarations
At present, any
else {...}
block is wrapped in an ENTER/LEAVE pair, evenif no new scope is warranted, which causes runtime inefficiency. For
example, this block doesn't need a scope but gets one anyway:
However, this behaviour means that an object like
$kaboom
will reliablybe destroyed when the
else
block is exited:In contrast,
if () {...}
orelsif () {...}
blocks default to nothaving an
ENTER/LEAVE
pair, which is more efficient but arguablyincorrect, as
$marvin
in this code won't be destroyed when theif
block is exited:
Exactly when it is destroyed is dependent upon where the next scope
exit happens to be, and this could be some ways away from its block.
This behaviour is also very brittle, as shown in this case where the
no-op
0;
statement causes - via a quirk of parsing - theif
block to be assigned an
ENTER/LEAVE
pair and so$marvin
will be destroyed when the block exits:
Whether a block gains a scope via
ENTER/LEAVE
pair or is just parentedby a
SCOPE
OP (that will be optimized away), is decided byPerl_op_scope
on the basis of theOPf_PARENS
flag on a block'sLINESEQ
OP. The parser always sets this flag forelse
blocks,a variety of circumstances determines whether it is set otherwise.
This commit makes two changes:
Removes the enforced use of the
OPf_PARENS
flag onelse
blocks.The same rules now apply to
if
,elsif
, andelse
, hopefully makingdestruction behaviour more predictable.
Changes the tokenizer such that occurrances of
my
,our
, andstate
set the
OPf_PARENS
flag, and the containing block will be wrapped inan
ENTER/LEAVE
pair.After this commit, neither of the
if
orelse
blocks in this examplewill have an unnecessary
ENTER/LEAVE
pair:And in this example, both objects will be destroyed when the relevant
block is exited:
Sadly, both blocks in this example will still have unnecessary scopes,
but fixing that is for a different PR:
There are two downsides to this commit:
It has the potential to change the destruction timing of objects created
in an
if
block and assigned to a lexical declared within the block, ifthe block hasn't ended up with an
ENTER/LEAVE
pair. As noted above though.that behaviour is very brittle and already sensitive to even minor changes
to the Perl code - and also to minor parser/optimization changes within the
interpreter.
The old
else
behaviour meant that the first statement'sNEXTSTATE
OPstays in place and consequently any error messages arising from the first
statement mention the correct (or closer) line number. The
if
/elsif
behaviour is more likely to cause the
NEXTSTATE
OP to be optimized away,causing any error messages to contain the line number for where the
if
or
elsif
keyword keyword occurred, which could be many lines away.Following this commit, first-line error messages will be just as bad
for an
else
block as they are forif
/elsif
blocks. However, that'sa separate issue and should not be a reason to avoid this commit.