This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #123802] Assertion failure with /$0{}/
authorFather Chrysostomos <sprout@cpan.org>
Sun, 1 Mar 2015 01:41:36 +0000 (17:41 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 1 Mar 2015 02:27:04 +0000 (18:27 -0800)
If the parser (perly.c) pops tokens when encountering a syntax error,
it can pop inner lexing scopes (which handle the contents of quote-
like operators).

If there is a pending token on the pending token stack, then the cur-
rent lexing state is LEX_KNOWNEXT.  It usually gets set to a pending
value stored in PL_lex_defer when the last pending token is emitted.

If scopes are exited when there is a pending token, then the state is
reset, since it is localised, even thought we still have a token pend-
ing.  We have code to account for that and still emit the pending
token.  (See 7aa8cb0dec1.)  But the pending lexing state is still
used after the pending token is emitted.  So we can end up with
LEX_INTERPEND when there is no inner lexing scope.

LEX_INTERPEND will cause sublex_done (in toke.c) to be called, which
does a LEAVE.  If the current scope does not belong to it, we end up
exiting a scope set up by the parser, which frees the parser stack
(via SAVEDESTRUCTOR_X and clear_yystack in perly.c).  The parser is
still using the stack, which holds reference counts on the CV in
PL_compcv, so reference counts get screwed up.

We need to check whether we have a proper lexing scope set up if the
lexing state is LEX_INTERPEND.

This is a follow-up to f4460c6f7a, which was a similar bug, but
occurred with LEX_INTERPCONCAT, rather than LEX_INTERPEND.

t/op/lex.t
toke.c

index dffd0b2..06d069a 100644 (file)
@@ -7,7 +7,7 @@ use warnings;
 
 BEGIN { chdir 't' if -d 't'; require './test.pl'; }
 
-plan(tests => 20);
+plan(tests => 21);
 
 {
     no warnings 'deprecated';
@@ -177,3 +177,11 @@ fresh_perl_is(
    { stderr => 1 },
   '"@{" [perl #123712]'
 );
+
+fresh_perl_is(
+  '/$0{}/',
+  'syntax error at - line 1, near "{}"' . "\n" .
+  "Execution of - aborted due to compilation errors.\n",
+   { stderr => 1 },
+  '/$0{}/ with no newline [perl #123712]'
+);
diff --git a/toke.c b/toke.c
index db511c5..33ae20f 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -4493,6 +4493,14 @@ Perl_yylex(pTHX)
        /* FALLTHROUGH */
 
     case LEX_INTERPEND:
+       /* Treat state as LEX_NORMAL if we have no inner lexing scope.
+          XXX This hack can be removed if we stop setting PL_lex_state to
+          LEX_KNOWNEXT, as can the hack under LEX_INTREPCONCAT below.  */
+       if (UNLIKELY(!PL_lex_inwhat)) {
+           PL_lex_state = LEX_NORMAL;
+           break;
+       }
+
        if (PL_lex_dojoin) {
            const U8 dojoin_was = PL_lex_dojoin;
            PL_lex_dojoin = FALSE;