Permalink
Browse files

Fix block migration in thunk gen; skids++ for the assist

Phixes #1212
Fixes RT#130575: https://rt.perl.org/Ticket/Display.html?id=130575
Fixes RT#132337: https://rt.perl.org/Ticket/Display.html?id=132337
Fixes RT#131548: https://rt.perl.org/Ticket/Display.html?id=131548
Fixes RT#132211: https://rt.perl.org/Ticket/Display.html?id=132211
Fixes RT#126569: https://rt.perl.org/Ticket/Display.html?id=126569
Fixes RT#128054: https://rt.perl.org/Ticket/Display.html?id=128054
Fixes RT#126413: https://rt.perl.org/Ticket/Display.html?id=126413
Fixes RT#126984: https://rt.perl.org/Ticket/Display.html?id=126984
Fixes RT#132172: https://rt.perl.org/Ticket/Display.html?id=132172

All of the bugs in the tickets are caused by mis-scoped blocks
resulting in p6capturelex failing to set outers correctly, and thus
resulting in issues with updating of values in lexicals. The
mis-scoping occurs during migration of blocks when we're generating
thunks with causes being one or several of:

1) WhateverCode QAST missing `statement_id` annotation, resuling in
  migration predicate being false, and thus not migrating the QAST
2) andthen/notandthen/orelse thunks being nested into one another
  instead of being alongside each other, as these operators aren't
  chained and instead are called with all the thunks present in
  a single operator call
3) Missing truthy `in_stmt_mod` annotation on first argument of the
  andthen/notandthen/orelse calls. These aren't thunked for the
  operator call, but they can have thunks due to other constructs
  present in them, so they need the annotation for proper migration
4) Present truthy `in_stmt_mod` annotation on non-first arguments
  of the andthen/notandthen/orelse calls. This occurs when we
  have one of these operators as a conditional in one of the
  statement modifiers. Mis-migration occurs because despite being
  in non-first argument of the operator, the migrator still thinks
  we're directly in statement modifier code
5) We can have sub-staments within our thunks that get an increased
  statement ID, but the migrator only migrates blocks with the same
  statement ID as the thunk itself, missing substatements

Fix all the issues with:

- Add `statement_id` annotation to WhateverCode QAST
- Annotate andthen/notandthen/orelse thunks and make migrator skip
  them, which avoids unwanted nesting
- Specially annotate first argument of andthen/notandthen/orelse
  ops with `in_stmt_mod_andnotelse` annotation to ensure we
  migrate any thunks inside appropriately. We can't just re-use
  `in_stmt_mod` annotation here, since we need to address cause
  (4) above as well, so we have two separate annotations and a
  somewhat convoluted migrator condition that covers all the cases
- Look for statement IDs equal to *or greater* than our thunk.
  This ensures all substatement inside our thunk get migrated right,
  as only blocks that have greater ID than us at this point in time
  would all be from within our thunk.
  • Loading branch information...
zoffixznet committed Jan 26, 2018
1 parent a5c2398 commit 1ee89b54074e80c0753a120d679c6265bd8d5d1f
Showing with 37 additions and 4 deletions.
  1. +37 −4 src/Perl6/Actions.nqp
@@ -7167,14 +7167,34 @@ class Perl6::Actions is HLL::Actions does STDActions {
$past
}

sub mark_blocks_as_andnotelse_first_arg($ast) {
if $ast && nqp::can($ast, 'ann') && $ast.ann('past_block') {
$ast.ann('past_block').annotate: 'in_stmt_mod_andnotelse', 1;
$ast.ann('past_block').annotate: 'in_stmt_mod', 0;
}
elsif nqp::istype($ast, QAST::Op)
|| nqp::istype($ast, QAST::Stmt)
|| nqp::istype($ast, QAST::Stmts) {
mark_blocks_as_andnotelse_first_arg($_) for @($ast)
}
}

sub thunkity_thunk($/,$thunky,$past,@clause) {
my int $i := 0;
my int $e := nqp::elems(@clause);
my int $te := nqp::chars($thunky);
my $type := nqp::substr($thunky,0,1);
my $andnotelse_thunk := nqp::istype($past, QAST::Op)
&& $past.op eq 'call'
&& ( $past.name eq '&infix:<andthen>'
|| $past.name eq '&infix:<notandthen>'
|| $past.name eq '&infix:<orelse>');

while $i < $e {
my $ast := @clause[$i];
$ast := $ast.ast if nqp::can($ast,'ast'); # reduce already passes ast...
mark_blocks_as_andnotelse_first_arg($ast)
if $andnotelse_thunk && $i == 0;

if $type eq 'T' || $type eq 'B' || $type eq 'A' {
my $argast := $ast;
@@ -7217,7 +7237,10 @@ class Perl6::Actions is HLL::Actions does STDActions {
}
elsif $type eq 'b' { # thunk and topicalize to a block
unless $ast.ann('bare_block') || $ast.ann('past_block') {
$ast := block_closure(make_topic_block_ref(@clause[$i], $ast, migrate_stmt_id => $*STATEMENT_ID));
$ast := block_closure(make_topic_block_ref(
@clause[$i], $ast, :$andnotelse_thunk,
migrate_stmt_id => $*STATEMENT_ID,
));
}
$past.push($ast);
}
@@ -9186,14 +9209,22 @@ class Perl6::Actions is HLL::Actions does STDActions {
$block);
}

sub make_topic_block_ref($/, $past, :$copy, :$migrate_stmt_id) {
sub make_topic_block_ref(
$/, $past, :$copy, :$migrate_stmt_id, :$andnotelse_thunk,
) {
my $block := $*W.push_lexpad($/);
$block.annotate: 'andnotelse_thunk', 1 if $andnotelse_thunk;

$block[0].push(QAST::Var.new( :name('$_'), :scope('lexical'), :decl('var') ));
$block.push($past);
$*W.pop_lexpad();
if nqp::defined($migrate_stmt_id) {
migrate_blocks($*W.cur_lexpad(), $block, -> $b {
!$b.ann('in_stmt_mod') && ($b.ann('statement_id') // -1) == $migrate_stmt_id
( (! $b.ann('in_stmt_mod_andnotelse') && $andnotelse_thunk)
|| (! $b.ann('in_stmt_mod') && ! $andnotelse_thunk)
)
&& ($b.ann('statement_id') // -1) >= $migrate_stmt_id
&& ! $b.has_ann('andnotelse_thunk')
});
}
($*W.cur_lexpad())[0].push($block);
@@ -9571,7 +9602,9 @@ class Perl6::Actions is HLL::Actions does STDActions {
my int $i := 0;
my @params;
my @old_args;
my $block := QAST::Block.new(QAST::Stmts.new(), $past);
my $block := QAST::Block.new(
QAST::Stmts.new(), $past
).annotate_self: 'statement_id', $*STATEMENT_ID;
$*W.cur_lexpad()[0].push($block);
while $i < $e {
my $old := $past[$i];

0 comments on commit 1ee89b5

Please sign in to comment.