Skip to content
Permalink
Browse files

Give 6.d correct `is rw` Proxy semantics

We had a long-standing bug where Proxy would not be decontainerized
when returning from a routine. This behavior should only apply for `is
rw` routines, but accidentally applied to all of them. This was fixed
while changing decontrv handling to use a spesh plugin, but it turned
out some modules depended on the wrong behavior, so the bug was then
emulated in the spesh plugin.

With the first 6.d-supporting release, we'll take the opportunity to
have the fix in 6.d, while compiling programs doing `use v6.c` to use
the previous buggy behavior.

This resolves issue #2113.
  • Loading branch information...
jnthn committed Nov 19, 2018
1 parent 49f07ab commit 7d37f9aaf0ef0290709679568dac0b6110c43506
Showing with 53 additions and 34 deletions.
  1. +12 −6 src/Perl6/Actions.nqp
  2. +1 −1 src/Perl6/Optimizer.nqp
  3. +24 −19 src/vm/moar/Perl6/Ops.nqp
  4. +16 −8 src/vm/moar/spesh-plugins.nqp
@@ -116,7 +116,7 @@ sub wanted($ast,$by) {
$ast[0] := WANTED($ast[0], $byby) if nqp::elems(@($ast));
$ast.wanted(1);
}
elsif $ast.op eq 'p6decontrv' {
elsif $ast.op eq 'p6decontrv' || $ast.op eq 'p6decontrv_6c' {
$ast[1] := WANTED($ast[1], $byby) if nqp::elems(@($ast));
$ast.wanted(1);
}
@@ -335,7 +335,7 @@ sub unwanted($ast, $by) {
$ast[0] := UNWANTED($ast[0], $byby) if nqp::elems(@($ast));
$ast.sunk(1);
}
elsif $ast.op eq 'p6decontrv' {
elsif $ast.op eq 'p6decontrv' || $ast.op eq 'p6decontrv_6c' {
$ast[1] := UNWANTED($ast[1], $byby) if nqp::elems(@($ast));
$ast.sunk(1);
}
@@ -859,7 +859,7 @@ register_op_desugar('p6var', -> $qast {
:op('call'),
QAST::Op.new(
:op('speshresolve'),
QAST::SVal.new( :value('decontrv') ),
QAST::SVal.new( :value($qast[1] eq '6c' ?? 'decontrv_6c' !! 'decontrv') ),
QAST::Var.new( :name($result), :scope('local') )
),
QAST::Var.new( :name($result), :scope('local') ),
@@ -3812,6 +3812,12 @@ class Perl6::Actions is HLL::Actions does STDActions {
method routine_declarator:sym<method>($/) { make $<method_def>.ast; }
method routine_declarator:sym<submethod>($/) { make $<method_def>.ast; }

sub decontrv_op() {
$*W.lang-ver-before('d') && nqp::getcomp('perl6').backend.name eq 'moar'
?? 'p6decontrv_6c'
!! 'p6decontrv'
}

method routine_def($/) {
my $block;

@@ -3829,7 +3835,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
}
if is_clearly_returnless($block) {
$block[1] := QAST::Op.new(
:op('p6decontrv'),
:op(decontrv_op()),
QAST::WVal.new( :value($*DECLARAND) ),
$block[1]);
$block[1] := wrap_return_type_check($block[1], $*DECLARAND);
@@ -4267,7 +4273,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
}
if is_clearly_returnless($past) {
$past[1] := QAST::Op.new(
:op('p6decontrv'),
:op(decontrv_op()),
QAST::WVal.new( :value($*DECLARAND) ),
$past[1]);
$past[1] := wrap_return_type_check($past[1], $*DECLARAND);
@@ -10071,7 +10077,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
:op<handlepayload>,
# If we fall off the bottom, decontainerize if
# rw not set.
QAST::Op.new( :op('p6decontrv'), QAST::WVal.new( :value($*DECLARAND) ), $past ),
QAST::Op.new( :op(decontrv_op()), QAST::WVal.new( :value($*DECLARAND) ), $past ),
'RETURN',
QAST::Op.new( :op<lastexpayload> )
),
@@ -1347,7 +1347,7 @@ class Perl6::Optimizer {
}

# May be able to eliminate some decontrv operations.
if $optype eq 'p6decontrv' {
if $optype eq 'p6decontrv' || $optype eq 'p6decontrv_6c' {
# If it's rw, don't need to decont at all.
my $value := $op[1];
return $value if $op[0].value.rw;
@@ -443,29 +443,34 @@ $ops.add_hll_op('perl6', 'p6typecheckrv', -> $qastcomp, $op {
nqp::die('p6dtypecheckrv expects a QAST::WVal as its second child');
}
});
$ops.add_hll_op('perl6', 'p6decontrv', -> $qastcomp, $op {
my $is_rw;
if nqp::istype($op[0], QAST::WVal) {
$is_rw := nqp::istrue($op[0].value.rw);
}
else {
nqp::die('p6decontrv expects a QAST::WVal as its first child');
}
if $is_rw {
$qastcomp.as_mast($op[1])
}
else {
my $type := nqp::getcodeobj(&get_binder)().get_return_type($op[0].value);
if !nqp::isnull($type) && nqp::objprimspec(nqp::decont($type)) -> int $prim {
if $prim == 1 { $qastcomp.as_mast($op[1], :want($MVM_reg_int64)) }
elsif $prim == 2 { $qastcomp.as_mast($op[1], :want($MVM_reg_num64)) }
else { $qastcomp.as_mast($op[1], :want($MVM_reg_str)) }
sub decontrv_op($version) {
-> $qastcomp, $op {
my $is_rw;
if nqp::istype($op[0], QAST::WVal) {
$is_rw := nqp::istrue($op[0].value.rw);
}
else {
$qastcomp.as_mast(QAST::Op.new( :op('p6decontrv_internal'), $op[1] ));
nqp::die('p6decontrv expects a QAST::WVal as its first child');
}
if $is_rw {
$qastcomp.as_mast($op[1])
}
else {
my $type := nqp::getcodeobj(&get_binder)().get_return_type($op[0].value);
if !nqp::isnull($type) && nqp::objprimspec(nqp::decont($type)) -> int $prim {
if $prim == 1 { $qastcomp.as_mast($op[1], :want($MVM_reg_int64)) }
elsif $prim == 2 { $qastcomp.as_mast($op[1], :want($MVM_reg_num64)) }
else { $qastcomp.as_mast($op[1], :want($MVM_reg_str)) }
}
else {
$qastcomp.as_mast(QAST::Op.new( :op("p6decontrv_internal"), $op[1], $version ));
}
}
}
});
}
$ops.add_hll_op('perl6', 'p6decontrv', decontrv_op(''));
$ops.add_hll_op('perl6', 'p6decontrv_6c', decontrv_op('6c'));

$ops.add_hll_op('perl6', 'p6setautothreader', :inlinable, -> $qastcomp, $op {
$qastcomp.as_mast(
QAST::Op.new( :op('callmethod'), :name('set_autothreader'),
@@ -81,21 +81,14 @@ sub identity($obj) { $obj }
}
}

nqp::speshreg('perl6', 'decontrv', sub ($rv) {
sub decontrv_plugin($rv) {
$Iterable := nqp::gethllsym('perl6', 'Iterable') if nqp::isnull($Iterable);
nqp::speshguardtype($rv, nqp::what_nd($rv));
if nqp::isconcrete_nd($rv) && nqp::iscont($rv) {
# Guard that it's concrete, so this only applies for container
# instances.
nqp::speshguardconcrete($rv);

# This emulates a bug where Proxy was never decontainerized no
# matter what. The ecosystem came to depend on that, so we will
# accept it for now. We need to revisit this in the future.
if nqp::eqaddr(nqp::what_nd($rv), Proxy) {
return &identity;
}

# If it's a Scalar container then we can optimize further.
if nqp::eqaddr(nqp::what_nd($rv), Scalar) {
# Grab the descriptor.
@@ -128,6 +121,21 @@ sub identity($obj) { $obj }
}
return nqp::isnull($rv) ?? &mu !! &identity;
}
}

nqp::speshreg('perl6', 'decontrv', &decontrv_plugin);
nqp::speshreg('perl6', 'decontrv_6c', -> $rv {
# This emulates a bug where Proxy was never decontainerized no
# matter what. The ecosystem came to depend on that, so we will
# accept it for now. We need to revisit this in the future.
if nqp::eqaddr(nqp::what_nd($rv), Proxy) && nqp::isconcrete_nd($rv) {
nqp::speshguardtype($rv, nqp::what_nd($rv));
nqp::speshguardconcrete($rv);
&identity
}
else {
decontrv_plugin($rv)
}
});
}

0 comments on commit 7d37f9a

Please sign in to comment.
You can’t perform that action at this time.