diff --git a/src/Perl6/Actions.nqp b/src/Perl6/Actions.nqp index 8b3a53d68d5..d6bb13cc301 100644 --- a/src/Perl6/Actions.nqp +++ b/src/Perl6/Actions.nqp @@ -667,6 +667,275 @@ register_op_desugar('p6forstmt', -> $qast { QAST::WVal.new( :value($qast.ann('Nil')) ) ) }); +register_op_desugar('p6scalarfromdesc', -> $qast { + my $desc := QAST::Node.unique('descriptor'); + my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) ); + my $default_cont_spec := nqp::gethllsym('perl6', 'default_cont_spec'); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($desc), :scope('local'), :decl('var') ), + $qast[0] + ), + QAST::Op.new( + :op('unless'), + QAST::Op.new( + :op('isconcrete'), + QAST::Var.new( :name($desc), :scope('local') ), + ), + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($desc), :scope('local') ), + QAST::WVal.new( :value($default_cont_spec) ) + ) + ), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( :op('create'), $Scalar ), + $Scalar, + QAST::SVal.new( :value('$!descriptor') ), + QAST::Var.new( :name($desc), :scope('local') ) + ), + $Scalar, + QAST::SVal.new( :value('$!value') ), + QAST::Op.new( + :op('callmethod'), :name('default'), + QAST::Var.new( :name($desc), :scope('local') ) + ) + ) + ) +}); +register_op_desugar('p6scalarwithvalue', -> $qast { + my $desc := QAST::Node.unique('descriptor'); + my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) ); + my $default_cont_spec := nqp::gethllsym('perl6', 'default_cont_spec'); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($desc), :scope('local'), :decl('var') ), + $qast[0] + ), + QAST::Op.new( + :op('p6assign'), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( :op('create'), $Scalar ), + $Scalar, + QAST::SVal.new( :value('$!descriptor') ), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('isconcrete'), + QAST::Var.new( :name($desc), :scope('local') ), + ), + QAST::Var.new( :name($desc), :scope('local') ), + QAST::WVal.new( :value($default_cont_spec) ) + ) + ), + $qast[1] + ) + ) +}); +register_op_desugar('p6recont_ro', -> $qast { + my $result := QAST::Node.unique('result'); + my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) ); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($result), :scope('local'), :decl('var') ), + $qast[0] + ), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('isconcrete_nd'), + QAST::Var.new( :name($result), :scope('local') ) + ), + QAST::Op.new( + :op('isrwcont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( :op('create'), $Scalar ), + $Scalar, + QAST::SVal.new( :value('$!value') ), + QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + QAST::Var.new( :name($result), :scope('local') ) + ) + ) +}); +register_op_desugar('p6var', -> $qast { + my $result := QAST::Node.unique('result'); + my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) ); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($result), :scope('local'), :decl('var') ), + $qast[0] + ), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('isconcrete_nd'), + QAST::Var.new( :name($result), :scope('local') ) + ), + QAST::Op.new( + :op('iscont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( :op('create'), $Scalar ), + $Scalar, + QAST::SVal.new( :value('$!value') ), + QAST::Var.new( :name($result), :scope('local') ) + ), + QAST::Var.new( :name($result), :scope('local') ) + ) + ) +}); +{ + my $is_moar; + register_op_desugar('p6decontrv_internal', -> $qast { + unless nqp::isconcrete($is_moar) { + $is_moar := nqp::getcomp('perl6').backend.name eq 'moar'; + } + if $is_moar { + my $result := QAST::Node.unique('result'); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($result), :scope('local'), :decl('var') ), + QAST::Op.new( :op('wantdecont'), $qast[0] ) + ), + QAST::Op.new( + :op('call'), + QAST::Op.new( + :op('speshresolve'), + QAST::SVal.new( :value('decontrv') ), + QAST::Var.new( :name($result), :scope('local') ) + ), + QAST::Var.new( :name($result), :scope('local') ), + ) + ) + } + else { + my $result := QAST::Node.unique('result'); + my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) ); + my $Iterable := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Iterable')) ); + QAST::Stmt.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($result), :scope('local'), :decl('var') ), + QAST::Op.new( :op('wantdecont'), $qast[0] ) + ), + QAST::Op.new( + # If it's a container... + :op('if'), + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('isconcrete_nd'), + QAST::Var.new( :name($result), :scope('local') ) + ), + QAST::Op.new( + :op('iscont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + # It's a container; is it an rw one? + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('isrwcont'), + QAST::Var.new( :name($result), :scope('local') ) + ), + # Yes; does it contain an Iterable? If so, rewrap it. If + # not, strip it. + QAST::Op.new( + :op('if'), + QAST::Op.new( + :op('istype'), + QAST::Var.new( :name($result), :scope('local') ), + $Iterable + ), + QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( :op('create'), $Scalar ), + $Scalar, + QAST::SVal.new( :value('$!value') ), + QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($result), :scope('local') ) + ) + ), + # Not rw, so leave container in place. + QAST::Var.new( :name($result), :scope('local') ) + ), + # Not a container, so just hand back value + QAST::Var.new( :name($result), :scope('local') ) + ) + ) + } + }); +} +{ + my $is_moar; + register_op_desugar('p6assign', -> $qast { + unless nqp::isconcrete($is_moar) { + $is_moar := nqp::getcomp('perl6').backend.name eq 'moar'; + } + if $is_moar { + my $cont := QAST::Node.unique('assign_cont'); + my $value := QAST::Node.unique('assign_value'); + QAST::Stmts.new( + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($cont), :scope('local'), :decl('var') ), + $qast[0] + ), + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($value), :scope('local'), :decl('var') ), + QAST::Op.new( :op('decont'), $qast[1] ) + ), + QAST::Op.new( + :op('call'), + QAST::Op.new( + :op('speshresolve'), + QAST::SVal.new( :value('assign') ), + QAST::Var.new( :name($cont), :scope('local') ), + QAST::Var.new( :name($value), :scope('local') ), + ), + QAST::Var.new( :name($cont), :scope('local') ), + QAST::Var.new( :name($value), :scope('local') ), + ), + QAST::Var.new( :name($cont), :scope('local') ) + ) + } + else { + QAST::Op.new( :op('assign'), $qast[0], $qast[1] ) + } + }); +} sub can-use-p6forstmt($block) { my $past_block := $block.ann('past_block'); @@ -2365,7 +2634,7 @@ class Perl6::Actions is HLL::Actions does STDActions { my $pad := $*W.cur_lexpad(); my $sym := $pad.unique('once_'); my $mu := $*W.find_symbol(['Mu']); - my $descriptor := $*W.create_container_descriptor($mu, 1, $sym); + my $descriptor := $*W.create_container_descriptor($mu, $sym); my %info; %info := %info := $*W.find_symbol(['Scalar']); %info := %info := %info := %info := $mu; @@ -3328,7 +3597,7 @@ class Perl6::Actions is HLL::Actions does STDActions { $list.push($*W.build_container_past( %cont_info, $*W.create_container_descriptor( - %cont_info, 1, 'anon', %cont_info))); + %cont_info, 'anon', %cont_info))); } } @@ -3550,7 +3819,7 @@ class Perl6::Actions is HLL::Actions does STDActions { $have_is_type ?? [$is_type] !! [], $shape, :@post); my $descriptor := $*W.create_container_descriptor( - %cont_info, 1, $attrname, %cont_info); + %cont_info, $attrname, %cont_info); # Create meta-attribute and add it. my $metaattr := $*W.resolve_mo($/, $*PKGDECL ~ '-attr'); @@ -3622,7 +3891,7 @@ class Perl6::Actions is HLL::Actions does STDActions { $have_is_type ?? [$is_type] !! [], $shape, :@post); my $descriptor := $*W.create_container_descriptor( - %cont_info, 1, $varname || $name, %cont_info); + %cont_info, $varname || $name, %cont_info); # Install the container. my $cont := $*W.install_lexical_container($BLOCK, $name, %cont_info, $descriptor, @@ -5400,10 +5669,7 @@ class Perl6::Actions is HLL::Actions does STDActions { if nqp::existskey(%*PARAM_INFO, 'nominal_type') { $cur_pad[0].push(QAST::Var.new( :$name, :scope('lexical'), :decl('var'), :returns(%*PARAM_INFO) )); - %*PARAM_INFO := $*W.create_container_descriptor( - %*PARAM_INFO, 0, %*PARAM_INFO); - $cur_pad.symbol(%*PARAM_INFO, :descriptor(%*PARAM_INFO), - :type(%*PARAM_INFO)); + $cur_pad.symbol(%*PARAM_INFO, :type(%*PARAM_INFO)); } else { $cur_pad[0].push(QAST::Var.new( :name($name), :scope('lexical'), :decl('var') )); } @@ -7233,9 +7499,10 @@ class Perl6::Actions is HLL::Actions does STDActions { } elsif $var_sigil eq '$' { # If it's a $ scalar, we can assume it's some kind of scalar - # container with a container spec, so can go directly for the - # low level assign op. - $past := QAST::Op.new( :op('assign'), $lhs_ast, $rhs_ast ); + # container with a container spec, so can go directly for a + # Scalar assign op (via. a level of indirection so that any + # platform that wants to optimize this somewhat can). + $past := QAST::Op.new( :op('p6assign'), $lhs_ast, $rhs_ast ); } elsif nqp::istype($lhs_ast, QAST::Op) && $lhs_ast.op eq 'call' && ($lhs_ast.name eq '&postcircumfix:<[ ]>' || @@ -7407,7 +7674,7 @@ class Perl6::Actions is HLL::Actions does STDActions { %cont{'default_value'} := $zero.compile_time_value; %cont{'scalar_value'} := $zero.compile_time_value; $*W.install_lexical_container($*W.cur_lexpad(), $state, %cont, - $*W.create_container_descriptor(%cont{'bind_constraint'}, 1, $state), + $*W.create_container_descriptor(%cont{'bind_constraint'}, $state), :scope('state')); # Twiddle to make special-case RHS * work. @@ -8414,7 +8681,7 @@ class Perl6::Actions is HLL::Actions does STDActions { else { $right := $infixish.ast; $right.push(QAST::Op.new( - :op('assign'), + :op('p6assign'), QAST::Op.new( :op('p6scalarfromdesc'), QAST::Op.new( :op('null') ) ), QAST::Var.new( :name('$/'), :scope('lexical') ) )); @@ -8701,6 +8968,7 @@ class Perl6::Actions is HLL::Actions does STDActions { my $Sig := $*W.find_symbol(['Signature'], :setting-only); my $Param := $*W.find_symbol(['Parameter'], :setting-only); my $Iterable := $*W.find_symbol(['Iterable']); + my $Scalar := $*W.find_symbol(['Scalar']); my @p_objs := nqp::getattr($sig, $Sig, '@!params'); my int $i := 0; my int $n := nqp::elems(@params); @@ -8810,6 +9078,7 @@ class Perl6::Actions is HLL::Actions does STDActions { my int $is_generic := %info; my int $is_rw := $flags +& $SIG_ELEM_IS_RW; my int $spec := nqp::objprimspec($nomtype); + my $decont_name; if $spec && !%info { if $is_rw { $var.push(QAST::ParamTypeCheck.new(QAST::Op.new( @@ -8831,12 +9100,23 @@ class Perl6::Actions is HLL::Actions does STDActions { QAST::Var.new( :name($name), :scope('local') ) ))); + # We decont it once before the checks, to avoid doing so + # repeatedly. + $decont_name := QAST::Node.unique("__lowered_param_decont_"); + $var.push(QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($decont_name), :scope('local'), :decl('var') ), + QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($name), :scope('local') ) + ))); + # Type-check, unless it's Mu, in which case skip it. if $is_generic { my $genericname := $nomtype.HOW.name(%info); $var.push(QAST::ParamTypeCheck.new(QAST::Op.new( - :op('istype'), - QAST::Var.new( :name($name), :scope('local') ), + :op('istype_nd'), + QAST::Var.new( :name($decont_name), :scope('local') ), QAST::Var.new( :name($genericname), :scope ) ))); } elsif !($nomtype =:= $*W.find_symbol(['Mu'])) { @@ -8848,21 +9128,26 @@ class Perl6::Actions is HLL::Actions does STDActions { $var.push(QAST::Op.new( :op('if'), QAST::Op.new( - :op('istype'), - QAST::Var.new( :name($name), :scope('local') ), + :op('istype_nd'), + QAST::Var.new( :name($decont_name), :scope('local') ), QAST::WVal.new( :value($*W.find_symbol(['PositionalBindFailover'])) ) ), QAST::Op.new( :op('bind'), - QAST::Var.new( :name($name), :scope('local') ), + QAST::Var.new( :name($decont_name), :scope('local') ), QAST::Op.new( - :op('callmethod'), :name('cache'), - QAST::Var.new( :name($name), :scope('local') ) - )))); + :op('decont'), + QAST::Op.new( + :op('bind'), + QAST::Var.new( :name($name), :scope('local') ), + QAST::Op.new( + :op('callmethod'), :name('cache'), + QAST::Var.new( :name($decont_name), :scope('local') ) + )))))); } $var.push(QAST::ParamTypeCheck.new(QAST::Op.new( - :op('istype'), - QAST::Var.new( :name($name), :scope('local') ), + :op('istype_nd'), + QAST::Var.new( :name($decont_name), :scope('local') ), QAST::WVal.new( :value($nomtype) ) ))); } @@ -8871,14 +9156,14 @@ class Perl6::Actions is HLL::Actions does STDActions { $var.push(QAST::ParamTypeCheck.new(QAST::Op.new( :op('not_i'), QAST::Op.new( - :op('isconcrete'), - QAST::Var.new( :name($name), :scope('local') ) + :op('isconcrete_nd'), + QAST::Var.new( :name($decont_name), :scope('local') ) )))); } if %info { $var.push(QAST::ParamTypeCheck.new(QAST::Op.new( - :op('isconcrete'), - QAST::Var.new( :name($name), :scope('local') ) + :op('isconcrete_nd'), + QAST::Var.new( :name($decont_name), :scope('local') ) ))); } if $is_rw { @@ -8895,6 +9180,7 @@ class Perl6::Actions is HLL::Actions does STDActions { if $coerce_to.HOW.archetypes.generic { return 0; } + $decont_name := NQPMu; $var.push(QAST::Op.new( :op('unless'), QAST::Op.new( @@ -8914,6 +9200,7 @@ class Perl6::Actions is HLL::Actions does STDActions { # If it's optional, do any default handling. if $flags +& $SIG_ELEM_IS_OPTIONAL { + $decont_name := NQPMu; if nqp::existskey(%info, 'default_value') { my $wval := QAST::WVal.new( :value(%info) ); if %info { @@ -8972,8 +9259,9 @@ class Perl6::Actions is HLL::Actions does STDActions { $var.push( QAST::Op.new( :op, QAST::Var.new( :name(nqp::shift($iter)), :scope ), - QAST::Op.new( :op, - QAST::Var.new( :name($name), :scope ) ) + $decont_name + ?? QAST::Op.new( :op, QAST::Var.new( :name($decont_name), :scope ) ) + !! QAST::Op.new( :op, QAST::Var.new( :name($name), :scope ) ) ) ); } @@ -8985,10 +9273,12 @@ class Perl6::Actions is HLL::Actions does STDActions { $var.push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('self'), :scope('lexical') ), - QAST::Op.new( - :op('decont'), - QAST::Var.new( :name($name), :scope('local') ) - ))); + $decont_name + ?? QAST::Var.new( :name($decont_name), :scope('local') ) + !! QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($name), :scope('local') ) + ))); } # Bind to lexical if needed. @@ -9081,23 +9371,42 @@ class Perl6::Actions is HLL::Actions does STDActions { $var.push(QAST::Op.new( :op('bind'), WANTED(QAST::Var.new( :name(%info), :scope('lexical') ),'lower_signature/wrap'), - QAST::Op.new( - :op('assignunchecked'), - QAST::Op.new( - :op('p6scalarfromdesc'), - QAST::WVal.new( :value(%info) ) - ), - QAST::Var.new( :name($name), :scope('local') ) - ))); + nqp::existskey(%info, 'container_descriptor') + ?? QAST::Op.new( + :op('assignunchecked'), + QAST::Op.new( + :op('p6scalarfromdesc'), + QAST::WVal.new( :value(%info) ) + ), + QAST::Var.new( :name($decont_name || $name), :scope('local') ) + ) + !! QAST::Op.new( + :op('p6bindattrinvres'), + QAST::Op.new( + :op('create'), + QAST::WVal.new( :value($Scalar) ) + ), + QAST::WVal.new( :value($Scalar) ), + QAST::SVal.new( :value('$!value') ), + $decont_name + ?? QAST::Var.new( :name($decont_name), :scope('local') ) + !! QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($name), :scope('local') ) + ) + ) + )); } else { $var.push(QAST::Op.new( :op('bind'), WANTED(QAST::Var.new( :name(%info), :scope('lexical') ),'lower_signature'), - QAST::Op.new( - :op('decont'), - QAST::Var.new( :name($name), :scope('local') ) - ))); + $decont_name + ?? QAST::Var.new( :name($decont_name), :scope('local') ) + !! QAST::Op.new( + :op('decont'), + QAST::Var.new( :name($name), :scope('local') ) + ))); } # Take care we don't undo explicit $_ bindings. @@ -9376,8 +9685,7 @@ class Perl6::Actions is HLL::Actions does STDActions { my $param := hash( :variable_name('$_'), :nominal_type($*W.find_symbol(['Mu']))); if $copy { $param := $*W.create_container_descriptor( - $*W.find_symbol(['Mu']), 0, '$_' - ); + $*W.find_symbol(['Mu']), '$_'); } my $param_obj := $*W.create_parameter($/, $param); if $copy { $param_obj.set_copy() } else { $param_obj.set_raw() } diff --git a/src/Perl6/Metamodel/BOOTSTRAP.nqp b/src/Perl6/Metamodel/BOOTSTRAP.nqp index 03f64c5b443..186ef847915 100644 --- a/src/Perl6/Metamodel/BOOTSTRAP.nqp +++ b/src/Perl6/Metamodel/BOOTSTRAP.nqp @@ -472,8 +472,8 @@ my class Binder { } # If it's a scalar, we always need to wrap it into a new - # container and store it, for copy or ro case (the rw bit - # in the container descriptor takes care of the rest). + # container and store it; the container descriptor will be + # provided and make it rw if it's an `is copy`. else { my $new_cont := nqp::create(Scalar); nqp::bindattr($new_cont, Scalar, '$!descriptor', @@ -1121,6 +1121,231 @@ my class Binder { BEGIN { nqp::p6setbinder(Binder); } # We need it in for the next BEGIN block nqp::p6setbinder(Binder); # The load-time case. +# Container descriptors come here so that they can refer to Perl 6 types. +class ContainerDescriptor { + has $!of; + has str $!name; + has $!default; + has int $!dynamic; + + method BUILD(:$of, str :$name, :$default, int :$dynamic) { + $!of := $of; + $!name := $name; + $!default := $default; + $!dynamic := $dynamic; + } + + method of() { $!of } + method name() { $!name } + method default() { $!default } + method dynamic() { $!dynamic } + + method set_of($of) { $!of := $of; self } + method set_default($default) { $!default := $default; self } + method set_dynamic($dynamic) { $!dynamic := $dynamic; self } + + method is_generic() { + $!of.HOW.archetypes.generic + } + + method instantiate_generic($type_environment) { + my $ins_of := $!of.HOW.instantiate_generic($!of, $type_environment); + my $ins := nqp::clone(self); + nqp::bindattr($ins, $?CLASS, '$!of', $ins_of); + $ins + } +} +role ContainerDescriptor::Whence { + has $!next-descriptor; + + method next() { + my $next := $!next-descriptor; + nqp::isconcrete($next) + ?? $next + !! ($!next-descriptor := nqp::gethllsym('perl6', 'default_cont_spec')) + } + method of() { self.next.of } + method default() { self.next.default } + method dynamic() { self.next.dynamic } +} +class ContainerDescriptor::BindArrayPos does ContainerDescriptor::Whence { + has $!target; + has int $!pos; + + method new($desc, $target, int $pos) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos, + '$!target', $target); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos, + '$!pos', $pos); + $self + } + + method assigned($scalar) { + nqp::bindpos($!target, $!pos, $scalar); + } +} +class ContainerDescriptor::BindArrayPos2D does ContainerDescriptor::Whence { + has $!target; + has int $!one; + has int $!two; + + method new($desc, $target, int $one, int $two) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos2D, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos2D, + '$!target', $target); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos2D, + '$!one', $one); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos2D, + '$!two', $two); + $self + } + + method assigned($scalar) { + nqp::bindpos2d($!target, $!one, $!two, $scalar); + } +} +class ContainerDescriptor::BindArrayPos3D does ContainerDescriptor::Whence { + has $!target; + has int $!one; + has int $!two; + has int $!three; + + method new($desc, $target, int $one, int $two, int $three) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos3D, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindArrayPos3D, + '$!target', $target); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos3D, + '$!one', $one); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos3D, + '$!two', $two); + nqp::bindattr_i($self, ContainerDescriptor::BindArrayPos3D, + '$!three', $three); + $self + } + + method assigned($scalar) { + nqp::bindpos3d($!target, $!one, $!two, $!three, $scalar); + } +} +class ContainerDescriptor::BindArrayPosND does ContainerDescriptor::Whence { + has $!target; + has $!idxs; + + method new($desc, $target, $idxs) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindArrayPosND, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindArrayPosND, + '$!target', $target); + nqp::bindattr($self, ContainerDescriptor::BindArrayPosND, + '$!idxs', $idxs); + $self + } + + method assigned($scalar) { + nqp::bindposnd($!target, $!idxs, $scalar); + } +} +class ContainerDescriptor::BindHashPos does ContainerDescriptor::Whence { + has $!target; + has $!key; + + method new($desc, $target, $key) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindHashPos, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindHashPos, + '$!target', $target); + nqp::bindattr($self, ContainerDescriptor::BindHashPos, + '$!key', $key); + $self + } + + method assigned($scalar) { + my $hash := nqp::getattr($!target, Map, '$!storage'); + $hash := nqp::bindattr($!target, Map, '$!storage', nqp::hash()) + unless nqp::isconcrete($hash); + nqp::bindkey($hash, $!key, $scalar); + } +} +class ContainerDescriptor::BindObjHashKey does ContainerDescriptor::Whence { + has $!target; + has $!key; + has $!which; + has $!pair; + + method new($desc, $target, $key, $which, $pair) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::BindObjHashKey, + '$!next-descriptor', $desc); + nqp::bindattr($self, ContainerDescriptor::BindObjHashKey, + '$!target', $target); + nqp::bindattr($self, ContainerDescriptor::BindObjHashKey, + '$!key', $key); + nqp::bindattr($self, ContainerDescriptor::BindObjHashKey, + '$!which', $which); + nqp::bindattr($self, ContainerDescriptor::BindObjHashKey, + '$!pair', $pair); + $self + } + + method assigned($scalar) { + my $hash := nqp::getattr($!target, Map, '$!storage'); + $hash := nqp::bindattr($!target, Map, '$!storage', nqp::hash()) + unless nqp::isconcrete($hash); + nqp::bindkey($hash, $!which, $!pair.new($!key, $scalar)); + } +} +class ContainerDescriptor::VivifyArray does ContainerDescriptor::Whence { + has $!target; + has int $!pos; + + method new($target, int $pos) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::VivifyArray, + '$!target', $target); + nqp::bindattr_i($self, ContainerDescriptor::VivifyArray, + '$!pos', $pos); + $self + } + + method assigned($scalar) { + my $target := $!target; + my $array := nqp::isconcrete($target) + ?? $target + !! nqp::assign($target, Array.new); + $array.BIND-POS($!pos, $scalar); + } +} +class ContainerDescriptor::VivifyHash does ContainerDescriptor::Whence { + has $!target; + has $!key; + + method new($target, $key) { + my $self := nqp::create(self); + nqp::bindattr($self, ContainerDescriptor::VivifyHash, + '$!target', $target); + nqp::bindattr($self, ContainerDescriptor::VivifyHash, + '$!key', $key); + $self + } + + method assigned($scalar) { + my $target := $!target; + my $array := nqp::isconcrete($target) + ?? $target + !! nqp::assign($target, Hash.new); + $array.BIND-KEY($!key, $scalar); + } +} + # We stick all the declarative bits inside of a BEGIN, so they get # serialized. BEGIN { @@ -1190,8 +1415,7 @@ BEGIN { } } else { - my $cd := Perl6::Metamodel::ContainerDescriptor.new( - :of($type), :rw(1), :name($name)); + my $cd := ContainerDescriptor.new(:of($type), :$name); my $scalar := nqp::create(Scalar); nqp::bindattr($scalar, Scalar, '$!descriptor', $cd); nqp::bindattr($scalar, Scalar, '$!value', $type); @@ -1309,9 +1533,8 @@ BEGIN { $type.HOW.instantiate_generic($type, $type_environment)); my $cd_ins := $cd.instantiate_generic($type_environment); nqp::bindattr($ins, Attribute, '$!container_descriptor', $cd_ins); - my $avc_var := nqp::p6var($avc); - my $avc_copy := nqp::clone($avc_var); - my @avc_mro := $avc_var.HOW.mro($avc_var); + my $avc_copy := nqp::clone_nd($avc); + my @avc_mro := nqp::how_nd($avc).mro($avc); my int $i := 0; $i := $i + 1 while @avc_mro[$i].HOW.is_mixin(@avc_mro[$i]); nqp::bindattr($avc_copy, @avc_mro[$i], '$!descriptor', $cd_ins); @@ -1331,11 +1554,9 @@ BEGIN { # class Scalar is Any { # has Mu $!descriptor; # has Mu $!value; - # has Mu $!whence; Scalar.HOW.add_parent(Scalar, Any); Scalar.HOW.add_attribute(Scalar, BOOTSTRAPATTR.new(:name<$!descriptor>, :type(Mu), :package(Scalar))); Scalar.HOW.add_attribute(Scalar, BOOTSTRAPATTR.new(:name<$!value>, :type(Mu), :package(Scalar))); - Scalar.HOW.add_attribute(Scalar, BOOTSTRAPATTR.new(:name<$!whence>, :type(Mu), :package(Scalar))); Scalar.HOW.add_method(Scalar, 'is_generic', nqp::getstaticcode(sub ($self) { my $dcself := nqp::decont($self); nqp::getattr($dcself, Scalar, '$!descriptor').is_generic() @@ -1354,8 +1575,94 @@ BEGIN { })); Scalar.HOW.compose_repr(Scalar); - # Scalar needs to be registered as a container type. - nqp::setcontspec(Scalar, 'rakudo_scalar', nqp::null()); + # Scalar needs to be registered as a container type. Also provide the + # slow-path implementation of various container operations. + nqp::setcontspec(Scalar, 'rakudo_scalar', nqp::hash( + 'store', nqp::getstaticcode(sub ($cont, $val) { + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + if nqp::isconcrete($desc) { + $val := $desc.default if nqp::eqaddr($val.WHAT, Nil); + my $type := $desc.of; + if nqp::eqaddr($type, Mu) || nqp::istype($val, $type) { + nqp::bindattr($cont, Scalar, '$!value', $val); + unless nqp::eqaddr($desc.WHAT, ContainerDescriptor) { + $desc.assigned($cont); + nqp::bindattr($cont, Scalar, '$!descriptor', $desc.next); + } + } + else { + my %x := nqp::gethllsym('perl6', 'P6EX'); + if nqp::ishash(%x) { + %x($desc.name, $val, $type); + } + else { + nqp::die("Type check failed in assignment"); + } + } + } + else { + nqp::die("Cannot assign to a readonly variable or a value"); + } + }), + 'store_unchecked', nqp::getstaticcode(sub ($cont, $val) { + nqp::bindattr($cont, Scalar, '$!value', $val); + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + unless nqp::eqaddr($desc.WHAT, ContainerDescriptor) { + $desc.assigned($cont); + nqp::bindattr($cont, Scalar, '$!descriptor', $desc.next); + } + }), + 'cas', nqp::getstaticcode(sub ($cont, $expected, $val) { + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + if nqp::isconcrete($desc) { + $val := $desc.default if nqp::eqaddr($val.WHAT, Nil); + my $type := $desc.of; + if nqp::eqaddr($type, Mu) || nqp::istype($val, $type) { + nqp::casattr($cont, Scalar, '$!value', $expected, $val); + } + else { + my %x := nqp::gethllsym('perl6', 'P6EX'); + if nqp::ishash(%x) { + %x($desc.name, $val, $type); + } + else { + nqp::die("Type check failed in assignment"); + } + } + } + else { + nqp::die("Cannot assign to a readonly variable or a value"); + } + }), + 'atomic_store', nqp::getstaticcode(sub ($cont, $val) { + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + if nqp::isconcrete($desc) { + $val := $desc.default if nqp::eqaddr($val.WHAT, Nil); + my $type := $desc.of; + if nqp::eqaddr($type, Mu) || nqp::istype($val, $type) { + nqp::atomicbindattr($cont, Scalar, '$!value', $val); + } + else { + my %x := nqp::gethllsym('perl6', 'P6EX'); + if nqp::ishash(%x) { + %x($desc.name, $val, $type); + } + else { + nqp::die("Type check failed in assignment"); + } + } + } + else { + nqp::die("Cannot assign to a readonly variable or a value"); + } + }), + )); + + # Cache a single default Scalar container spec, to ensure we only get + # one of them. + Scalar.HOW.cache_add(Scalar, 'default_cont_spec', + ContainerDescriptor.new( + :of(Mu), :default(Any), :name('element'))); # Set up various native reference types. sub setup_native_ref_type($type, $primitive, $ref_kind) { @@ -1389,11 +1696,14 @@ BEGIN { Proxy.HOW.add_attribute(Proxy, BOOTSTRAPATTR.new(:name<&!FETCH>, :type(Mu), :package(Proxy))); Proxy.HOW.add_attribute(Proxy, BOOTSTRAPATTR.new(:name<&!STORE>, :type(Mu), :package(Proxy))); Proxy.HOW.add_method(Proxy, 'FETCH', ($PROXY_FETCH := nqp::getstaticcode(sub ($cont) { - nqp::decont( - nqp::getattr($cont, Proxy, '&!FETCH')(nqp::p6var($cont))) + my $var := nqp::create(Scalar); + nqp::bindattr($var, Scalar, '$!value', $cont); + nqp::decont(nqp::getattr($cont, Proxy, '&!FETCH')($var)) }))); Proxy.HOW.add_method(Proxy, 'STORE', ($PROXY_STORE := nqp::getstaticcode(sub ($cont, $val) { - nqp::getattr($cont, Proxy, '&!STORE')(nqp::p6var($cont), $val) + my $var := nqp::create(Scalar); + nqp::bindattr($var, Scalar, '$!value', $cont); + nqp::getattr($cont, Proxy, '&!STORE')($var, $val) }))); Proxy.HOW.add_method(Proxy, 'new', nqp::getstaticcode(sub ($type, :$FETCH!, :$STORE!) { my $cont := nqp::create(Proxy); @@ -1412,8 +1722,7 @@ BEGIN { # Attribute instance, complete with container descriptor and optional # auto-viv container. sub scalar_attr($name, $type, $package, :$associative_delegate, :$auto_viv_container = 1) { - my $cd := Perl6::Metamodel::ContainerDescriptor.new( - :of($type), :rw(1), :$name); + my $cd := ContainerDescriptor.new(:of($type), :$name); if $auto_viv_container { my $scalar := nqp::create(Scalar); nqp::bindattr($scalar, Scalar, '$!descriptor', $cd); @@ -1573,16 +1882,12 @@ BEGIN { nqp::atkey(%ex, 'X::Trait::Invalid')('is', 'rw', 'optional parameter', $varname); } } - my $cd := nqp::getattr($dcself, Parameter, '$!container_descriptor'); - if nqp::defined($cd) { $cd.set_rw(1) } nqp::bindattr_i($dcself, Parameter, '$!flags', $flags + $SIG_ELEM_IS_RW); $dcself })); Parameter.HOW.add_method(Parameter, 'set_copy', nqp::getstaticcode(sub ($self) { my $SIG_ELEM_IS_COPY := 512; my $dcself := nqp::decont($self); - my $cd := nqp::getattr($dcself, Parameter, '$!container_descriptor'); - if nqp::defined($cd) { $cd.set_rw(1) } nqp::bindattr_i($dcself, Parameter, '$!flags', nqp::getattr_i($dcself, Parameter, '$!flags') + $SIG_ELEM_IS_COPY); $dcself @@ -3159,7 +3464,7 @@ BEGIN { EXPORT::DEFAULT.WHO := Bool; EXPORT::DEFAULT.WHO := $false; EXPORT::DEFAULT.WHO := $true; - EXPORT::DEFAULT.WHO := Perl6::Metamodel::ContainerDescriptor; + EXPORT::DEFAULT.WHO := ContainerDescriptor; EXPORT::DEFAULT.WHO := Perl6::Metamodel::MethodDispatcher; EXPORT::DEFAULT.WHO := Perl6::Metamodel::MultiDispatcher; EXPORT::DEFAULT.WHO := Perl6::Metamodel::WrapDispatcher; @@ -3232,6 +3537,11 @@ nqp::neverrepossess(PROCESS.WHO); nqp::neverrepossess(nqp::getattr(PROCESS.WHO, Map, '$!storage')); nqp::bindhllsym('perl6', 'PROCESS', PROCESS); +# Stash Scalar and a default container spec away in the HLL state. +nqp::bindhllsym('perl6', 'Scalar', Scalar); +nqp::bindhllsym('perl6', 'default_cont_spec', + Scalar.HOW.cache_get(Scalar, 'default_cont_spec')); + # HLL configuration: interop, boxing and exit handling. nqp::sethllconfig('perl6', nqp::hash( 'int_box', Int, diff --git a/src/Perl6/Metamodel/ContainerDescriptor.nqp b/src/Perl6/Metamodel/ContainerDescriptor.nqp deleted file mode 100644 index c2f4330168c..00000000000 --- a/src/Perl6/Metamodel/ContainerDescriptor.nqp +++ /dev/null @@ -1,37 +0,0 @@ -class Perl6::Metamodel::ContainerDescriptor { - has $!of; - has int $!rw; - has str $!name; - has $!default; - has int $!dynamic; - - method BUILD(:$of, :$rw, :$name, :$default, :$dynamic) { - $!of := $of; - $!rw := $rw; - $!name := $name; - $!default := $default; - $!dynamic := $dynamic; - } - - method of() { $!of } - method rw() { $!rw } - method name() { $!name } - method default() { $!default } - method dynamic() { $!dynamic } - - method set_of($of) { $!of := $of; self } - method set_rw($rw) { $!rw := $rw; self } - method set_default($default) { $!default := $default; self } - method set_dynamic($dynamic) { $!dynamic := $dynamic; self } - - method is_generic() { - $!of.HOW.archetypes.generic - } - - method instantiate_generic($type_environment) { - my $ins_of := $!of.HOW.instantiate_generic($!of, $type_environment); - my $ins := nqp::clone(self); - nqp::bindattr($ins, $?CLASS, '$!of', $ins_of); - $ins - } -} diff --git a/src/Perl6/Optimizer.nqp b/src/Perl6/Optimizer.nqp index 8120de063cd..379870e9526 100644 --- a/src/Perl6/Optimizer.nqp +++ b/src/Perl6/Optimizer.nqp @@ -628,7 +628,7 @@ my class BlockVarOptimizer { next unless $scope eq 'lexical'; # Also ensure not dynamic. - my $dynamic := try nqp::getattr($qast.value, nqp::p6var($qast.value).WHAT, '$!descriptor').dynamic; + my $dynamic := try nqp::getattr($qast.value, nqp::what_nd($qast.value), '$!descriptor').dynamic; next if $dynamic; # Consider name. Can't lower if it's used by any nested blocks. @@ -1132,7 +1132,7 @@ class Perl6::Optimizer { # Let's see if we can catch a type mismatch in assignment at compile-time. # Especially with Num, Rat, and Int there's often surprises at run-time. - if ($optype eq 'assign' || $optype eq 'assign_n' || $optype eq 'assign_i') + if ($optype eq 'p6assign' || $optype eq 'assign_n' || $optype eq 'assign_i') && nqp::istype($op[0], QAST::Var) && ($op[0].scope eq 'lexical' || $op[0].scope eq 'lexicalref') { if nqp::istype($op[1], QAST::Want) { @@ -1914,7 +1914,7 @@ class Perl6::Optimizer { $is-always-definite := 1; } elsif $sigil eq '$' { - $assignop := 'assign'; + $assignop := 'p6assign'; } elsif $sigil eq '@' || $sigil eq '%' { $assignop := 'p6store'; @@ -1979,7 +1979,7 @@ class Perl6::Optimizer { $op.annotate_self: 'METAOP_opt_result', 1; $op.returns: $assignee.returns - if $assignop ne 'assign' + if $assignop ne 'p6assign' && nqp::objprimspec($assignee.returns); my $*NO-COMPILE-TIME-THROWAGE := 1; @@ -2236,24 +2236,46 @@ class Perl6::Optimizer { # Any literal in void context deserves a warning. if $!void_context && +@($want) == 3 && $want.node - && ! $want.ann('sink-quietly') { - + && !$want.ann('sink-quietly') { my str $warning; + my $no-sink; + if $want[1] eq 'Ss' && nqp::istype($want[2], QAST::SVal) { - $warning := qq[Useless use of constant string "] - ~ nqp::escape($want[2].node // $want[2].value) - ~ qq[" in sink context]; + $warning := 'constant string "' + ~ nqp::escape($want[2].node // $want[2].value) + ~ '"' } elsif $want[1] eq 'Ii' && nqp::istype($want[2], QAST::IVal) { - $warning := qq[Useless use of constant integer ] - ~ ($want[2].node // $want[2].value) - ~ qq[ in sink context]; + $warning := 'constant integer ' + ~ ($want[2].node // $want[2].value); } elsif $want[1] eq 'Nn' && nqp::istype($want[2], QAST::NVal) { - $warning := qq[Useless use of constant floating-point number ] - ~ ($want[2].node // $want[2].value) ~ qq[ in sink context]; + $warning := 'constant floating-point number ' + ~ ($want[2].node // $want[2].value); + } + elsif $want[1] eq 'v' && nqp::istype($want[2], QAST::Op) { +# R#2040 +# - QAST::Op(p6capturelex) +# - QAST::Op(callmethod clone) +# - QAST::WVal(Sub...) + if $want[0].op eq 'p6capturelex' { + my $op := $want[0][0]; + if $op.op eq 'callmethod' && $op.name eq 'clone' { + $op := $op[0]; + if nqp::istype($op, QAST::WVal) && nqp::istype( + $op.value, + $!symbols.find_in_setting("Sub") + ) && !$op.value.name { + $warning := qq[anonymous sub, did you forget to provide a name?]; + $no-sink := 1; + } + } + } } + if $warning { + $warning := "Useless use of " ~ $warning; + $warning := $warning ~ qq[ in sink context] unless $no-sink; $warning := $warning ~ ' (use Nil instead to suppress this warning)' if $want.okifnil; note($warning) if $!debug; $!problems.add_worry($want, $warning); diff --git a/src/Perl6/World.nqp b/src/Perl6/World.nqp index 60298e90c3e..22e906fd067 100644 --- a/src/Perl6/World.nqp +++ b/src/Perl6/World.nqp @@ -1266,7 +1266,8 @@ class Perl6::World is HLL::World { :auth-matcher(%opts // $true), :api-matcher(%opts // $true), :version-matcher(%opts // $true), - :source-line-number($line) + :source-line-number($line), + :source-file-name(self.current_file) ); self.add_object($spec); my $registry := self.find_symbol(['CompUnit', 'RepositoryRegistry'], :setting-only); @@ -1568,9 +1569,9 @@ class Perl6::World is HLL::World { } # Creates a new container descriptor and adds it to the SC. - method create_container_descriptor($of, $rw, $name, $default = $of, $dynamic = nqp::chars($name) > 2 && nqp::eqat($name, '*', 1)) { + method create_container_descriptor($of, $name, $default = $of, $dynamic = nqp::chars($name) > 2 && nqp::eqat($name, '*', 1)) { my $cd_type := self.find_symbol(['ContainerDescriptor'], :setting-only); - my $cd := $cd_type.new( :$of, :$rw, :$name, :$default, :$dynamic ); + my $cd := $cd_type.new( :$of, :$name, :$default, :$dynamic ); self.add_object($cd); $cd } @@ -1796,7 +1797,7 @@ class Perl6::World is HLL::World { 'scalar_value', $WHAT, ); my $desc := - self.create_container_descriptor($Mu, 1, $name, $WHAT, 1); + self.create_container_descriptor($Mu, $name, $WHAT, 1); my $cont := self.build_container_and_add_to_sc(%info, $desc); @@ -1869,7 +1870,7 @@ class Perl6::World is HLL::World { my %cont_info := self.container_type_info(NQPMu, $var, $*OFTYPE ?? [$*OFTYPE.ast] !! [], []); %cont_info := self.find_symbol(['Any'], :setting-only); - my $descriptor := self.create_container_descriptor(%cont_info, 1, $name); + my $descriptor := self.create_container_descriptor(%cont_info, $name); nqp::die("auto_declare_var") unless nqp::objectid($*PACKAGE) == nqp::objectid($*LEAF.package); self.install_lexical_container($BLOCK, $name, %cont_info, $descriptor, @@ -2059,6 +2060,7 @@ class Perl6::World is HLL::World { # Walk parameters, setting up parameter objects. my $default_type := self.find_symbol([$default_type_name]); + my $param_type := self.find_symbol(['Parameter'], :setting-only); my @param_objs; my %seen_names; for @params { @@ -2096,26 +2098,26 @@ class Perl6::World is HLL::World { $_, $lexpad, $default_type_name); } + # Create parameter object and apply any traits. + my $param_obj := self.create_parameter($/, $_); + self.apply_traits($_, $param_obj) if $_; + # Add variable as needed. + my int $flags := nqp::getattr_i($param_obj, $param_type, '$!flags'); my $varname := $_; - if $varname { + if $varname && ($flags +& $SIG_ELEM_IS_RW || $flags +& $SIG_ELEM_IS_COPY) { my %sym := $lexpad.symbol($varname); if +%sym && !nqp::existskey(%sym, 'descriptor') { - $_ := self.create_container_descriptor( - $_, $_ ?? 1 !! 0, $varname); - $lexpad.symbol($varname, :descriptor($_)); + my $desc := self.create_container_descriptor($_, $varname); + $_ := $desc; + nqp::bindattr($param_obj, $param_type, '$!container_descriptor', $desc); + $lexpad.symbol($varname, :descriptor($desc)); } } - # Create parameter object and apply any traits. - my $param_obj := self.create_parameter($/, $_); - self.apply_traits($_, $param_obj) if $_; - # If it's natively typed and we got "is rw" set, need to mark the # container as being a lexical ref. if $varname && nqp::objprimspec($_) { - my $param_type := self.find_symbol(['Parameter'], :setting-only); - my int $flags := nqp::getattr_i($param_obj, $param_type, '$!flags'); if $flags +& $SIG_ELEM_IS_RW { for @($lexpad[0]) { if nqp::istype($_, QAST::Var) && $_.name eq $varname { @@ -3911,7 +3913,7 @@ class Perl6::World is HLL::World { %info := self.find_symbol(['Associative'], :setting-only); %info := $mu; self.install_lexical_container($*UNIT, '!INIT_VALUES', %info, - self.create_container_descriptor($mu, 1, '!INIT_VALUES')); + self.create_container_descriptor($mu, '!INIT_VALUES')); } $*UNIT[0].push(QAST::Op.new( :op('callmethod'), :name('BIND-KEY'), diff --git a/src/core/Any.pm6 b/src/core/Any.pm6 index 06401a877d0..68458a7ccae 100644 --- a/src/core/Any.pm6 +++ b/src/core/Any.pm6 @@ -251,30 +251,10 @@ my class Any { # declared in BOOTSTRAP proto method AT-POS(|) is nodal {*} multi method AT-POS(Any:U \SELF: int \pos) is raw { - nqp::p6bindattrinvres( - my $scalar, - Scalar, - '$!whence', - -> { nqp::if( - nqp::isconcrete(SELF), - SELF, - (SELF = Array.new) - ).BIND-POS(pos, $scalar) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::VivifyArray.new(SELF, pos)) } multi method AT-POS(Any:U \SELF: Int:D \pos) is raw { - nqp::p6bindattrinvres( - my $scalar, - Scalar, - '$!whence', - -> { nqp::if( - nqp::isconcrete(SELF), - SELF, - (SELF = Array.new) - ).BIND-POS(pos, $scalar) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::VivifyArray.new(SELF, pos)) } multi method AT-POS(Any:U: Num:D \pos) is raw { nqp::isnanorinf(pos) @@ -405,21 +385,7 @@ my class Any { # declared in BOOTSTRAP ) } multi method AT-KEY(Any:U \SELF: \key) is raw { - nqp::p6bindattrinvres( - my $scalar, - Scalar, - '$!whence', - # NOTE: even though the signature indicates a non-concrete SELF, - # by the time the below code is executed, it *may* have become - # concrete: and then we don't want the execution to reset it to - # an empty Hash. - -> { nqp::if( - nqp::isconcrete(SELF), - SELF, - (SELF = nqp::create(Hash)) - ).BIND-KEY(key, $scalar) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::VivifyHash.new(SELF, key)) } proto method BIND-KEY(|) is nodal {*} diff --git a/src/core/Array.pm6 b/src/core/Array.pm6 index c423d4a2e98..53ae2ebf229 100644 --- a/src/core/Array.pm6 +++ b/src/core/Array.pm6 @@ -29,8 +29,7 @@ my class Array { # declared in BOOTSTRAP } method push(Mu \value --> Nil) { - nqp::push($!target, - nqp::assign(nqp::p6scalarfromdesc($!descriptor), value)); + nqp::push($!target, nqp::p6scalarwithvalue($!descriptor, value)); } method append(IterationBuffer:D $buffer --> Nil) { @@ -40,10 +39,9 @@ my class Array { # declared in BOOTSTRAP (my int $i = -1), nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), - nqp::push($!target,nqp::assign( - nqp::p6scalarfromdesc($!descriptor), - nqp::atpos($buffer,$i) - )) + nqp::push($!target, + nqp::p6scalarwithvalue($!descriptor,nqp::atpos($buffer,$i)) + ) ) ) ) @@ -139,12 +137,8 @@ my class Array { # declared in BOOTSTRAP ) } method hole(int $i) { - nqp::p6bindattrinvres( - (my \v := nqp::p6scalarfromdesc($!descriptor)), - Scalar, - '$!whence', - -> { nqp::bindpos($!reified,$i,v) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + $!descriptor, $!reified, $i)) } method done() is raw { $!todo := nqp::bindattr($!array,List,'$!todo',Mu); @@ -279,7 +273,7 @@ my class Array { # declared in BOOTSTRAP nqp::stmts( # only a single element nqp::push( buffer, - nqp::assign(nqp::p6scalarfromdesc($!descriptor),iterable) + nqp::p6scalarwithvalue($!descriptor,iterable) ), nqp::bindattr(self,List,'$!todo',Mu) ), @@ -309,7 +303,7 @@ my class Array { # declared in BOOTSTRAP nqp::stmts( nqp::push( (my \buffer = nqp::create(IterationBuffer)), - nqp::assign(nqp::p6scalarfromdesc($!descriptor), item) + nqp::p6scalarwithvalue($!descriptor, item) ), nqp::bindattr(self,List,'$!todo',Mu), nqp::p6bindattrinvres(self,List,'$!reified',buffer) @@ -337,13 +331,8 @@ my class Array { # declared in BOOTSTRAP multi method AT-POS(Int:D \pos) { nqp::ifnull( nqp::atpos(nqp::getattr(self,List,'$!reified'),pos), - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!descriptor)), - Scalar, - '$!whence', - -> { nqp::bindpos( - nqp::getattr(self,List,'$!reified'),pos,$scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + $!descriptor, nqp::getattr(self,List,'$!reified'), pos)) ) } method default() { $!descriptor.default } @@ -443,66 +432,61 @@ my class Array { # declared in BOOTSTRAP method shape() { (*,) } multi method AT-POS(Array:D: int $pos) is raw { - nqp::if( - nqp::isge_i($pos,0) - && nqp::isconcrete(nqp::getattr(self,List,'$!reified')), - nqp::ifnull( - nqp::atpos(nqp::getattr(self,List,'$!reified'),$pos), - self!AT_POS_SLOW($pos) - ), - self!AT_POS_SLOW($pos) - ) + my $reified := nqp::getattr(self, List, '$!reified'); + my $result := nqp::bitand_i(nqp::isge_i($pos, 0), nqp::isconcrete($reified)) + ?? nqp::atpos($reified, $pos) + !! nqp::null; + nqp::ifnull($result, self!AT_POS_SLOW($pos)) } # because this is a very hot path, we copied the code from the int candidate multi method AT-POS(Array:D: Int:D $pos) is raw { - nqp::if( - nqp::isge_i($pos,0) - && nqp::isconcrete(nqp::getattr(self,List,'$!reified')), - nqp::ifnull( - nqp::atpos(nqp::getattr(self,List,'$!reified'),$pos), - self!AT_POS_SLOW($pos) - ), - self!AT_POS_SLOW($pos) - ) + my $reified := nqp::getattr(self, List, '$!reified'); + my $result := nqp::bitand_i(nqp::isge_i($pos, 0), nqp::isconcrete($reified)) + ?? nqp::atpos($reified, $pos) + !! nqp::null; + nqp::ifnull($result, self!AT_POS_SLOW($pos)) } # handle any lookup that's not simple - method !AT_POS_SLOW(\pos) is raw { + method !AT_POS_SLOW(int $pos) is raw { nqp::if( - nqp::islt_i(pos, 0), - self!INDEX_OOR(pos), + nqp::islt_i($pos, 0), + self!INDEX_OOR($pos), nqp::if( nqp::isconcrete(my $reified := nqp::getattr(self,List,'$!reified')), nqp::if( - nqp::islt_i(pos,nqp::elems($reified)), - self!AT_POS_CONTAINER(pos), # it's a hole + nqp::islt_i($pos,nqp::elems($reified)), + self!AT_POS_CONTAINER($pos), # it's a hole nqp::if( # too far out, try reifying nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), nqp::stmts( - $todo.reify-at-least(nqp::add_i(pos,1)), + $todo.reify-at-least(nqp::add_i($pos,1)), nqp::ifnull( - nqp::atpos($reified,pos), # reified ok - self!AT_POS_CONTAINER(pos) # reifier didn't reach + nqp::atpos($reified,$pos), # reified ok + self!AT_POS_CONTAINER($pos) # reifier didn't reach ) ), - self!AT_POS_CONTAINER(pos) # create an outlander + self!AT_POS_CONTAINER($pos) # create an outlander ) ), # no reified, implies no todo nqp::stmts( # create reified nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)), - self!AT_POS_CONTAINER(pos) # create an outlander + self!AT_POS_CONTAINER($pos) # create an outlander ) ) ) } method !AT_POS_CONTAINER(int $pos) is raw { - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!descriptor)), - Scalar, - '$!whence', - -> { nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,$scalar) } - ) + my $desc := $!descriptor; + my $scalar := nqp::create(Scalar); + nqp::bindattr($scalar, Scalar, '$!value', nqp::isnull($desc) + ?? Any + !! nqp::getattr($desc, ContainerDescriptor, '$!default')); + nqp::bindattr($scalar, Scalar, '$!descriptor', + ContainerDescriptor::BindArrayPos.new( + $desc, nqp::getattr(self,List,'$!reified'), $pos)); + $scalar } multi method ASSIGN-POS(Array:D: int $pos, Mu \assignee) { @@ -518,10 +502,7 @@ my class Array { # declared in BOOTSTRAP nqp::if( nqp::isconcrete(nqp::getattr(self, List, '$!todo')), self!ASSIGN_POS_SLOW_PATH($pos, assignee), - nqp::assign( - nqp::bindpos(reified, $pos, nqp::p6scalarfromdesc($!descriptor)), - assignee - ) + nqp::bindpos(reified, $pos, nqp::p6scalarwithvalue($!descriptor, assignee)) ), nqp::assign(target, assignee) ) @@ -545,10 +526,7 @@ my class Array { # declared in BOOTSTRAP nqp::if( nqp::isconcrete(nqp::getattr(self, List, '$!todo')), self!ASSIGN_POS_SLOW_PATH($pos, assignee), - nqp::assign( - nqp::bindpos(reified, $ipos, nqp::p6scalarfromdesc($!descriptor)), - assignee - ) + nqp::bindpos(reified, $pos, nqp::p6scalarwithvalue($!descriptor, assignee)) ), nqp::assign(target, assignee) ) @@ -722,7 +700,7 @@ my class Array { # declared in BOOTSTRAP nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), - nqp::assign(nqp::p6scalarfromdesc($!descriptor),value) + nqp::p6scalarwithvalue($!descriptor,value) ), self ) @@ -748,7 +726,7 @@ my class Array { # declared in BOOTSTRAP nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), - nqp::assign(nqp::p6scalarfromdesc($!descriptor),value) + nqp::p6scalarwithvalue($!descriptor,value) ), self ), @@ -794,7 +772,7 @@ my class Array { # declared in BOOTSTRAP nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), - nqp::assign(nqp::p6scalarfromdesc($!descriptor),value) + nqp::p6scalarwithvalue($!descriptor,value) ), self ) @@ -813,7 +791,7 @@ my class Array { # declared in BOOTSTRAP nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), - nqp::assign(nqp::p6scalarfromdesc($!descriptor),value) + nqp::p6scalarwithvalue($!descriptor,value) ), self ), diff --git a/src/core/CompUnit/DependencySpecification.pm6 b/src/core/CompUnit/DependencySpecification.pm6 index 00fa9c3b40a..2ffda670e5b 100644 --- a/src/core/CompUnit/DependencySpecification.pm6 +++ b/src/core/CompUnit/DependencySpecification.pm6 @@ -1,6 +1,7 @@ class CompUnit::DependencySpecification { has str $.short-name is required; has int $.source-line-number = 0; + has str $.source-file-name = ''; has str $.from = 'Perl6'; has $.version-matcher = True; has $.auth-matcher = True; diff --git a/src/core/CompUnit/RepositoryRegistry.pm6 b/src/core/CompUnit/RepositoryRegistry.pm6 index dc4406196fa..1d2db873257 100644 --- a/src/core/CompUnit/RepositoryRegistry.pm6 +++ b/src/core/CompUnit/RepositoryRegistry.pm6 @@ -334,7 +334,7 @@ class CompUnit::RepositoryRegistry { } } - sub short-id2class(Str:D $short-id) { + sub short-id2class(Str:D $short-id) is rw { state %short-id2class; state $lock = Lock.new; diff --git a/src/core/Exception.pm6 b/src/core/Exception.pm6 index b775c37fcdb..7d29c81ad88 100644 --- a/src/core/Exception.pm6 +++ b/src/core/Exception.pm6 @@ -2958,9 +2958,10 @@ my class X::CompUnit::UnsatisfiedDependency is Exception { method message() { my $name = $.specification.short-name; my $line = $.specification.source-line-number; + my $file = $.specification.source-file-name; is-core($name) ?? "{$name} is a builtin type, not an external module" - !! "Could not find $.specification at line $line in:\n" + !! "Could not find $.specification at $file:$line in:\n" ~ $*REPO.repo-chain.map(*.Str).join("\n").indent(4) ~ ($.specification ~~ / $=.+ '::from' $ / ?? "\n\nIf you meant to use the :from adverb, use" diff --git a/src/core/Failure.pm6 b/src/core/Failure.pm6 index 39d997b56b1..0597d823693 100644 --- a/src/core/Failure.pm6 +++ b/src/core/Failure.pm6 @@ -57,7 +57,7 @@ my class Failure is Nil { Bool::False; } multi method Bool(Failure:D:) { $!handled = 1; Bool::False; } - method handled() { + method handled() is rw { Proxy.new( FETCH => { #?if moar diff --git a/src/core/Hash.pm6 b/src/core/Hash.pm6 index 95c6d7a2296..bb824557345 100644 --- a/src/core/Hash.pm6 +++ b/src/core/Hash.pm6 @@ -47,18 +47,7 @@ my class Hash { # declared in BOOTSTRAP } method !AT_KEY_CONTAINER(Str:D \key) is raw { - nqp::p6bindattrinvres( - (my \v := nqp::p6scalarfromdesc($!descriptor)), - Scalar, - '$!whence', - -> { nqp::bindkey( - nqp::if( - nqp::isconcrete(nqp::getattr(self,Map,'$!storage')), - nqp::getattr(self,Map,'$!storage'), - nqp::bindattr(self,Map,'$!storage',nqp::hash) - ),key,v) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindHashPos.new($!descriptor, self, key)) } multi method AT-KEY(Hash:D: Str:D \key) is raw { @@ -528,36 +517,13 @@ my class Hash { # declared in BOOTSTRAP my role TypedHash[::TValue, ::TKey] does Associative[TValue] { method keyof () { TKey } method AT-KEY(::?CLASS:D: TKey \key) is raw { + my str $which = nqp::unbox_s(key.WHICH); + my Mu \storage = nqp::getattr(self,Map,'$!storage'); nqp::if( - nqp::isconcrete(nqp::getattr(self,Map,'$!storage')), - nqp::if( - nqp::existskey(nqp::getattr(self,Map,'$!storage'), - (my str $which = nqp::unbox_s(key.WHICH))), - nqp::getattr( - nqp::atkey(nqp::getattr(self,Map,'$!storage'),$which), - Pair,'$!value'), - nqp::p6bindattrinvres( - (my \v := nqp::p6scalarfromdesc( - nqp::getattr(self,Hash,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindkey(nqp::getattr(self,Map,'$!storage'), - $which,Pair.new(key,v)); v } - ) - ), - nqp::p6bindattrinvres( - (my \vv := nqp::p6scalarfromdesc( - nqp::getattr(self,Hash,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindkey( - nqp::if( - nqp::isconcrete(nqp::getattr(self,Map,'$!storage')), - nqp::getattr(self,Map,'$!storage'), - nqp::bindattr(self,Map,'$!storage',nqp::hash) - ), - nqp::unbox_s(key.WHICH), Pair.new(key,vv)); vv } - ) + nqp::isconcrete(storage) && nqp::existskey(storage, $which), + nqp::getattr(nqp::atkey(storage, $which), Pair, '$!value'), + nqp::p6scalarfromdesc(ContainerDescriptor::BindObjHashKey.new( + nqp::getattr(self, Hash, '$!descriptor'), self, key, $which, Pair)) ) } diff --git a/src/core/Junction.pm6 b/src/core/Junction.pm6 index 811a9cad9b7..deb58198d81 100644 --- a/src/core/Junction.pm6 +++ b/src/core/Junction.pm6 @@ -40,9 +40,20 @@ my class Junction { # declared in BOOTSTRAP (nqp::iseq_s($btype,"all") || nqp::iseq_s($btype,"none")) && (nqp::iseq_s($atype,"any") || nqp::iseq_s($atype,"one")), nqp::stmts( # need to be swapped - (my $tmp := nqp::decont(a)), - (a = b), - (b = $tmp), + nqp::bindattr( + (my $a := nqp::clone(nqp::decont(b))), + Junction, + '$!storage', + nqp::getattr(nqp::decont(a),Junction,'$!storage') + ), + nqp::bindattr( + (my $b := nqp::clone(nqp::decont(a))), + Junction, + '$!storage', + nqp::getattr(nqp::decont(b),Junction,'$!storage') + ), + (a = $a), + (b = $b), 0 # not same, now swapped ) ) diff --git a/src/core/Rakudo/Iterator.pm6 b/src/core/Rakudo/Iterator.pm6 index 9b7b17b980d..0916cb0e34c 100644 --- a/src/core/Rakudo/Iterator.pm6 +++ b/src/core/Rakudo/Iterator.pm6 @@ -2496,12 +2496,8 @@ class Rakudo::Iterator { method new(\arr, Mu \des) { nqp::create(self)!SET-SELF(arr, des) } method !hole(int $i) is raw { - nqp::p6bindattrinvres( - (my \v := nqp::p6scalarfromdesc($!descriptor)), - Scalar, - '$!whence', - -> { nqp::bindpos($!reified,$i,v) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + $!descriptor, $!reified, $i)) } method pull-one() is raw { nqp::ifnull( diff --git a/src/core/Scalar.pm6 b/src/core/Scalar.pm6 index a68ed9c75cc..7a74855ff00 100644 --- a/src/core/Scalar.pm6 +++ b/src/core/Scalar.pm6 @@ -2,7 +2,6 @@ my class Scalar { # declared in BOOTSTRAP # class Scalar is Any # has Mu $!descriptor; # has Mu $!value; - # has Mu $!whence; method new(|) { X::Cannot::New.new(class => self.WHAT).throw } diff --git a/src/core/SetHash.pm6 b/src/core/SetHash.pm6 index fcc523e4af6..86a6f5114e0 100644 --- a/src/core/SetHash.pm6 +++ b/src/core/SetHash.pm6 @@ -148,7 +148,7 @@ my class SetHash does Setty { } multi method values(SetHash:D:) { Seq.new(class :: does Rakudo::Iterator::Mappy { - method pull-one() { + method pull-one() is rw { nqp::if( $!iter, proxy(nqp::shift($!iter),$!hash), diff --git a/src/core/Shaped1Array.pm6 b/src/core/Shaped1Array.pm6 index 88f4eea4080..0d6455a2675 100644 --- a/src/core/Shaped1Array.pm6 +++ b/src/core/Shaped1Array.pm6 @@ -18,15 +18,10 @@ ) } sub AT-POS-CONTAINER(\array, int \one) is raw { - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc( - nqp::getattr(array,Array,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindpos( - nqp::getattr(array,List,'$!reified'), - one, $scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + nqp::getattr(array, Array, '$!descriptor'), + nqp::getattr(array, List, '$!reified'), + one)) } multi method ASSIGN-POS(::?CLASS:D: int \one, \value) { @@ -207,12 +202,8 @@ ), nqp::ifnull( nqp::atpos($!reified,$!pos), - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!desc)), - Scalar, - '$!whence', - -> { nqp::bindpos($!reified,$!pos,$scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + $!desc, $!reified, $!pos)) ), IterationEnd ) @@ -226,12 +217,8 @@ $target.push( nqp::ifnull( nqp::atpos($!reified,$i), - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!desc)), - Scalar, - '$!whence', - -> { nqp::bindpos($!reified,$i,$scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos.new( + $!desc, $!reified, $i)) ) ) ), diff --git a/src/core/Shaped2Array.pm6 b/src/core/Shaped2Array.pm6 index 751e43d5736..49b1e116386 100644 --- a/src/core/Shaped2Array.pm6 +++ b/src/core/Shaped2Array.pm6 @@ -18,15 +18,10 @@ ) } sub AT-POS-CONTAINER(\array, int \one, int \two) is raw { - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc( - nqp::getattr(array,Array,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindpos2d( - nqp::getattr(array,List,'$!reified'), - one, two, $scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos2D.new( + nqp::getattr(array, Array, '$!descriptor'), + nqp::getattr(array, List, '$!reified'), + one, two)) } multi method ASSIGN-POS(::?CLASS:D: int \one, int \two, \value) { diff --git a/src/core/Shaped3Array.pm6 b/src/core/Shaped3Array.pm6 index 36c284c9383..c9981a605e7 100644 --- a/src/core/Shaped3Array.pm6 +++ b/src/core/Shaped3Array.pm6 @@ -18,15 +18,10 @@ ) } sub AT-POS-CONTAINER(\array, int \one, int \two, int \three) is raw { - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc( - nqp::getattr(array,Array,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindpos3d( - nqp::getattr(array,List,'$!reified'), - one, two, three, $scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPos2D.new( + nqp::getattr(array, Array, '$!descriptor'), + nqp::getattr(array, List, '$!reified'), + one, two, three)) } multi method ASSIGN-POS(::?CLASS:D: int \one, int \two, int \three, \value) { diff --git a/src/core/ShapedArray.pm6 b/src/core/ShapedArray.pm6 index 99150a2303d..db7a8a5a53e 100644 --- a/src/core/ShapedArray.pm6 +++ b/src/core/ShapedArray.pm6 @@ -25,13 +25,8 @@ ), (my $element := nqp::ifnull( nqp::atposnd($reified,$idxs), # found it - nqp::p6bindattrinvres( # create container - (my $scalar := nqp::p6scalarfromdesc( - nqp::getattr(self,Array,'$!descriptor'))), - Scalar, - '$!whence', - -> { nqp::bindposnd($reified,$idxs,$scalar) } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPosND.new( + nqp::getattr(array, Array, '$!descriptor'), $reified, $idxs)) )), nqp::if( nqp::elems($indices), @@ -460,19 +455,12 @@ self.indices, nqp::ifnull( nqp::atposnd($!list,$!indices), - nqp::stmts( - # By the time the block gets executed, the $!indices - # may be at the next iteration already or even reset - # because we reached the end. So we need to make - # a copy of the indices now. - (my $indices := nqp::clone($!indices)), - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!desc)), - Scalar, - '$!whence', - -> { nqp::bindposnd($!list,$indices,$scalar) } - ) - ) + # By the time the block gets executed, the $!indices + # may be at the next iteration already or even reset + # because we reached the end. So we need to make + # a copy of the indices now. + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPosND.new( + $!desc, $!list, nqp::clone($!indices))) ) ) } @@ -507,19 +495,12 @@ method result() is raw { nqp::ifnull( nqp::atposnd($!list,$!indices), - nqp::stmts( - # By the time the block gets executed, the $!indices - # may be at the next iteration already or even reset - # because we reached the end. So we need to make - # a copy of the indices now. - (my $indices := nqp::clone($!indices)), - nqp::p6bindattrinvres( - (my $scalar := nqp::p6scalarfromdesc($!desc)), - Scalar, - '$!whence', - -> { nqp::bindposnd($!list,$indices,$scalar) } - ) - ) + # By the time the block gets executed, the $!indices + # may be at the next iteration already or even reset + # because we reached the end. So we need to make + # a copy of the indices now. + nqp::p6scalarfromdesc(ContainerDescriptor::BindArrayPosND.new( + $!desc, $!list, nqp::clone($!indices))) ) } }.new(self) diff --git a/src/core/Stash.pm6 b/src/core/Stash.pm6 index dbace3e949f..d172f4c497a 100644 --- a/src/core/Stash.pm6 +++ b/src/core/Stash.pm6 @@ -7,16 +7,7 @@ my class Stash { # declared in BOOTSTRAP nqp::getattr(self,Map,'$!storage') && nqp::existskey(nqp::getattr(self,Map,'$!storage'),$key), nqp::atkey(nqp::getattr(self,Map,'$!storage'),$key), - nqp::p6bindattrinvres( - my $v,Scalar,'$!whence', - -> { nqp::bindkey( - nqp::getattr(self,Map,'$!storage') - || nqp::bindattr(self,Map,'$!storage',nqp::hash), - $key, - $v - ) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindHashPos.new(Mu, self, $key)) ) } multi method AT-KEY(Stash:D: Str() $key, :$global_fallback!) is raw { @@ -31,16 +22,7 @@ my class Stash { # declared in BOOTSTRAP nqp::atkey(GLOBAL.WHO,$key), Failure.new("Could not find symbol '$key'") ), - nqp::p6bindattrinvres( - my $v,Scalar,'$!whence', - -> { nqp::bindkey( - nqp::getattr(self,Map,'$!storage') - || nqp::bindattr(self,Map,'$!storage',nqp::hash), - $key, - $v - ) - } - ) + nqp::p6scalarfromdesc(ContainerDescriptor::BindHashPos.new(Mu, self, $key)) ) ) } diff --git a/src/core/Str.pm6 b/src/core/Str.pm6 index 70d3743c5d6..60dbc9d16ec 100644 --- a/src/core/Str.pm6 +++ b/src/core/Str.pm6 @@ -3118,10 +3118,10 @@ multi sub substr(\what) { what.substr } multi sub substr(\what, \from) { what.substr(from) } multi sub substr(\what, \from, \chars) { what.substr(from,chars) } -proto sub substr-rw($, $?, $?, *%) {*} -multi sub substr-rw(\what) { what.substr-rw } -multi sub substr-rw(\what, \from) { what.substr-rw(from) } -multi sub substr-rw(\what, \from, \chars) { what.substr-rw(from,chars) } +proto sub substr-rw($, $?, $?, *%) is rw {*} +multi sub substr-rw(\what) is rw { what.substr-rw } +multi sub substr-rw(\what, \from) is rw { what.substr-rw(from) } +multi sub substr-rw(\what, \from, \chars) is rw { what.substr-rw(from,chars) } multi sub infix:(Str:D \a, Str:D \b) { nqp::p6bool( diff --git a/src/core/TypedArray.pm6 b/src/core/TypedArray.pm6 index af3d7bdae7c..46681bd350f 100644 --- a/src/core/TypedArray.pm6 +++ b/src/core/TypedArray.pm6 @@ -49,8 +49,7 @@ sub set-descriptor(\list) is raw { nqp::stmts( nqp::bindattr(list,Array,'$!descriptor', - Perl6::Metamodel::ContainerDescriptor.new( - :of(TValue), :rw(1), :default(TValue)) + ContainerDescriptor.new(:of(TValue), :default(TValue)) ), list ) diff --git a/src/core/core_prologue.pm6 b/src/core/core_prologue.pm6 index 391f198eac5..463ff34dc18 100644 --- a/src/core/core_prologue.pm6 +++ b/src/core/core_prologue.pm6 @@ -24,6 +24,10 @@ my role Callable { ... } my role Iterable { ... } my role PositionalBindFailover { ... } +# Make Iterable available for the code-gen. +BEGIN nqp::bindhllsym('perl6', 'Iterable', Iterable); +nqp::bindhllsym('perl6', 'Iterable', Iterable); + # Set up Empty, which is a Slip created with an empty IterationBuffer (which # we also stub here). This is needed in a bunch of simple constructs (like if # with only one branch). diff --git a/src/vm/jvm/Perl6/Ops.nqp b/src/vm/jvm/Perl6/Ops.nqp index 020c841d60f..73607bc29ee 100644 --- a/src/vm/jvm/Perl6/Ops.nqp +++ b/src/vm/jvm/Perl6/Ops.nqp @@ -39,9 +39,7 @@ $ops.map_classlib_hll_op('perl6', 'p6box_n', $TYPE_P6OPS, 'p6box_n', [$RT_NUM], $ops.map_classlib_hll_op('perl6', 'p6box_s', $TYPE_P6OPS, 'p6box_s', [$RT_STR], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6bigint', $TYPE_P6OPS, 'p6bigint', [$RT_NUM], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6configposbindfailover', $TYPE_P6OPS, 'p6configposbindfailover', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); -$ops.map_classlib_hll_op('perl6', 'p6recont_ro', $TYPE_P6OPS, 'p6recont_ro', [$RT_OBJ], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6store', $TYPE_P6OPS, 'p6store', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); -$ops.map_classlib_hll_op('perl6', 'p6var', $TYPE_P6OPS, 'p6var', [$RT_OBJ], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6reprname', $TYPE_P6OPS, 'p6reprname', [$RT_OBJ], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6definite', $TYPE_P6OPS, 'p6definite', [$RT_OBJ], $RT_OBJ, :tc); $ops.add_hll_op('perl6', 'p6bindsig', :!inlinable, -> $qastcomp, $op { @@ -96,7 +94,21 @@ $ops.map_classlib_hll_op('perl6', 'p6setiterbuftype', $TYPE_P6OPS, 'p6setiterbuf $ops.map_classlib_hll_op('perl6', 'p6isbindable', $TYPE_P6OPS, 'p6isbindable', [$RT_OBJ, $RT_OBJ], $RT_INT, :tc); $ops.map_classlib_hll_op('perl6', 'p6bindcaptosig', $TYPE_P6OPS, 'p6bindcaptosig', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6typecheckrv', $TYPE_P6OPS, 'p6typecheckrv', [$RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); -$ops.map_classlib_hll_op('perl6', 'p6decontrv', $TYPE_P6OPS, 'p6decontrv', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); +$ops.add_hll_op('perl6', 'p6decontrv', :!inlinable, -> $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_jast($op[1]) + } + else { + $qastcomp.as_jast(QAST::Op.new( :op('p6decontrv_internal'), $op[1] )); + } +}); $ops.map_classlib_hll_op('perl6', 'p6capturelex', $TYPE_P6OPS, 'p6capturelex', [$RT_OBJ], $RT_OBJ, :tc, :!inlinable); $ops.map_classlib_hll_op('perl6', 'p6bindassert', $TYPE_P6OPS, 'p6bindassert', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc); $ops.map_classlib_hll_op('perl6', 'p6stateinit', $TYPE_P6OPS, 'p6stateinit', [], $RT_INT, :tc, :!inlinable); @@ -176,7 +188,6 @@ my $p6bool := -> $qastcomp, $op { $ops.result($il, $RT_OBJ); }; $ops.add_hll_op('perl6', 'p6bool', $p6bool); -$ops.map_classlib_hll_op('perl6', 'p6scalarfromdesc', $TYPE_P6OPS, 'p6scalarfromdesc', [$RT_OBJ], $RT_OBJ, :tc); $ops.add_hll_op('perl6', 'p6invokehandler', -> $qastcomp, $op { $qastcomp.as_jast(QAST::Op.new( :op('call'), $op[0], $op[1] )); }); diff --git a/src/vm/jvm/runtime/org/perl6/rakudo/RakOps.java b/src/vm/jvm/runtime/org/perl6/rakudo/RakOps.java index e4f838ff60f..a4199543df0 100644 --- a/src/vm/jvm/runtime/org/perl6/rakudo/RakOps.java +++ b/src/vm/jvm/runtime/org/perl6/rakudo/RakOps.java @@ -55,7 +55,6 @@ public static class GlobalExt { public SixModelObject EMPTYHASH; public RakudoJavaInterop rakudoInterop; public SixModelObject JavaHOW; - public SixModelObject defaultContainerDescriptor; boolean initialized; public GlobalExt(ThreadContext tc) {} @@ -71,9 +70,8 @@ public GlobalExt(ThreadContext tc) {} private static final int HINT_SIG_RETURNS = 1; private static final int HINT_SIG_CODE = 4; public static final int HINT_CD_OF = 0; - public static final int HINT_CD_RW = 1; - public static final int HINT_CD_NAME = 2; - public static final int HINT_CD_DEFAULT = 3; + public static final int HINT_CD_NAME = 1; + public static final int HINT_CD_DEFAULT = 2; public static SixModelObject p6init(ThreadContext tc) { GlobalExt gcx = key.getGC(tc); @@ -133,21 +131,6 @@ public static SixModelObject p6settypes(SixModelObject conf, ThreadContext tc) { gcx.True = conf.at_key_boxed(tc, "True"); gcx.Associative = conf.at_key_boxed(tc, "Associative"); gcx.JavaHOW = conf.at_key_boxed(tc, "Metamodel").st.WHO.at_key_boxed(tc, "JavaHOW"); - - SixModelObject defCD = gcx.ContainerDescriptor.st.REPR.allocate(tc, - gcx.ContainerDescriptor.st); - defCD.bind_attribute_boxed(tc, gcx.ContainerDescriptor, - "$!of", HINT_CD_OF, gcx.Mu); - tc.native_s = ""; - defCD.bind_attribute_native(tc, gcx.ContainerDescriptor, - "$!name", HINT_CD_NAME); - tc.native_i = 1; - defCD.bind_attribute_native(tc, gcx.ContainerDescriptor, - "$!rw", HINT_CD_RW); - defCD.bind_attribute_boxed(tc, gcx.ContainerDescriptor, - "$!default", HINT_CD_DEFAULT, gcx.Any); - gcx.defaultContainerDescriptor = defCD; - return conf; } @@ -354,84 +337,6 @@ public static SixModelObject p6store(SixModelObject cont, SixModelObject value, return cont; } - public static SixModelObject p6decontrv(SixModelObject routine, SixModelObject cont, ThreadContext tc) { - GlobalExt gcx = key.getGC(tc); - if (cont != null) { - if (isRWScalar(tc, gcx, cont)) { - routine.get_attribute_native(tc, gcx.Routine, "$!rw", HINT_ROUTINE_RW); - if (tc.native_i == 0) { - /* Recontainerize to RO. */ - SixModelObject roCont = gcx.Scalar.st.REPR.allocate(tc, gcx.Scalar.st); - roCont.bind_attribute_boxed(tc, gcx.Scalar, "$!value", - RakudoContainerSpec.HINT_value, - cont.st.ContainerSpec.fetch(tc, cont)); - return roCont; - } - } - else if (cont instanceof NativeRefInstance) { - routine.get_attribute_native(tc, gcx.Routine, "$!rw", HINT_ROUTINE_RW); - if (tc.native_i == 0) - return cont.st.ContainerSpec.fetch(tc, cont); - } - } - return cont; - } - - public static SixModelObject p6scalarfromdesc(SixModelObject desc, ThreadContext tc) { - GlobalExt gcx = key.getGC(tc); - - if ( Ops.isconcrete(desc, tc) == 0 ) - desc = gcx.defaultContainerDescriptor; - SixModelObject defVal = desc.get_attribute_boxed(tc, gcx.ContainerDescriptor, - "$!default", HINT_CD_DEFAULT); - - SixModelObject cont = gcx.Scalar.st.REPR.allocate(tc, gcx.Scalar.st); - cont.bind_attribute_boxed(tc, gcx.Scalar, "$!descriptor", - RakudoContainerSpec.HINT_descriptor, desc); - cont.bind_attribute_boxed(tc, gcx.Scalar, "$!value", RakudoContainerSpec.HINT_value, - defVal); - - return cont; - } - - public static SixModelObject p6recont_ro(SixModelObject cont, ThreadContext tc) { - GlobalExt gcx = key.getGC(tc); - if (isRWScalar(tc, gcx, cont)) { - SixModelObject roCont = gcx.Scalar.st.REPR.allocate(tc, gcx.Scalar.st); - roCont.bind_attribute_boxed(tc, gcx.Scalar, "$!value", - RakudoContainerSpec.HINT_value, - cont.st.ContainerSpec.fetch(tc, cont)); - return roCont; - } - return cont; - } - - private static boolean isRWScalar(ThreadContext tc, GlobalExt gcx, SixModelObject check) { - if (!(check instanceof TypeObject) && check.st.WHAT == gcx.Scalar) { - SixModelObject desc = check.get_attribute_boxed(tc, gcx.Scalar, "$!descriptor", - RakudoContainerSpec.HINT_descriptor); - if (desc == null) - return false; - desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!rw", HINT_CD_RW); - return tc.native_i != 0; - } - return false; - } - - public static SixModelObject p6var(SixModelObject cont, ThreadContext tc) { - if (cont != null && cont.st.ContainerSpec != null) { - GlobalExt gcx = key.getGC(tc); - SixModelObject wrapper = gcx.Scalar.st.REPR.allocate(tc, gcx.Scalar.st); - wrapper.bind_attribute_boxed(tc, gcx.Scalar, "$!value", - RakudoContainerSpec.HINT_value, - cont); - return wrapper; - } - else { - return cont; - } - } - public static SixModelObject p6reprname(SixModelObject obj, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); obj = Ops.decont(obj, tc); diff --git a/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerConfigurer.java b/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerConfigurer.java index 03b6736634b..e1e04a92419 100644 --- a/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerConfigurer.java +++ b/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerConfigurer.java @@ -1,6 +1,7 @@ package org.perl6.rakudo; import org.perl6.nqp.runtime.ThreadContext; +import org.perl6.nqp.runtime.*; import org.perl6.nqp.sixmodel.*; public class RakudoContainerConfigurer extends ContainerConfigurer { @@ -11,6 +12,18 @@ public void setContainerSpec(ThreadContext tc, STable st) { /* Configures the container spec with the specified info. */ public void configureContainerSpec(ThreadContext tc, STable st, SixModelObject config) { - /* Nothing to configure here. */ + RakudoContainerSpec cs = (RakudoContainerSpec)st.ContainerSpec; + cs.store = grabOneValue(tc, config, "store"); + cs.storeUnchecked = grabOneValue(tc, config, "store_unchecked"); + cs.cas = grabOneValue(tc, config, "cas"); + cs.atomicStore = grabOneValue(tc, config, "atomic_store"); } + private static SixModelObject grabOneValue(ThreadContext tc, SixModelObject config, String key) { + SixModelObject value = config.at_key_boxed(tc, key); + if (value == null) + throw ExceptionHandling.dieInternal(tc, + "Container spec must be configured with a '" + key + "'"); + return value; + } + } diff --git a/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerSpec.java b/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerSpec.java index f7db6b49007..bcaf6317ca0 100644 --- a/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerSpec.java +++ b/src/vm/jvm/runtime/org/perl6/rakudo/RakudoContainerSpec.java @@ -10,11 +10,18 @@ public class RakudoContainerSpec extends ContainerSpec { /* Container related hints. */ public static final int HINT_descriptor = 0; public static final int HINT_value = 1; - public static final int HINT_whence = 2; - /* Callsite descriptor for WHENCEs. */ - private static final CallSiteDescriptor WHENCE = new CallSiteDescriptor( - new byte[] { }, null); + /* Callsite descriptors. */ + private static final CallSiteDescriptor STORE = new CallSiteDescriptor( + new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); + private static final CallSiteDescriptor CAS = new CallSiteDescriptor( + new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); + + /* Callbacks. */ + public SixModelObject store; + public SixModelObject storeUnchecked; + public SixModelObject cas; + public SixModelObject atomicStore; /* Fetches a value out of a container. Used for decontainerization. */ public SixModelObject fetch(ThreadContext tc, SixModelObject cont) { @@ -31,62 +38,8 @@ public String fetch_s(ThreadContext tc, SixModelObject cont) { } /* Stores a value in a container. Used for assignment. */ - private static final CallSiteDescriptor storeThrower = new CallSiteDescriptor( - new byte[] { CallSiteDescriptor.ARG_STR, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); - private void checkStore(ThreadContext tc, SixModelObject cont, SixModelObject value) { - RakOps.GlobalExt gcx = RakOps.key.getGC(tc); - - long rw = 0; - SixModelObject desc = cont.get_attribute_boxed(tc, gcx.Scalar, - "$!descriptor", HINT_descriptor); - if (desc != null) { - desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!rw", RakOps.HINT_CD_RW); - rw = tc.native_i; - } - if (rw == 0) - if (desc != null) { - desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!name", RakOps.HINT_CD_NAME); - throw ExceptionHandling.dieInternal(tc, - "Cannot assign to a readonly variable (" + tc.native_s + ") or a value"); - } - else { - throw ExceptionHandling.dieInternal(tc, - "Cannot assign to a readonly variable or a value"); - } - - if (value.st.WHAT == gcx.Nil) { - value = desc.get_attribute_boxed(tc, - gcx.ContainerDescriptor, "$!default", RakOps.HINT_CD_DEFAULT); - } - SixModelObject of = desc.get_attribute_boxed(tc, - gcx.ContainerDescriptor, "$!of", RakOps.HINT_CD_OF); - long ok = of == gcx.Mu ? 1 : Ops.istype(value, of, tc); - if (ok == 0) { - desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!name", RakOps.HINT_CD_NAME); - String name = tc.native_s; - SixModelObject thrower = RakOps.getThrower(tc, "X::TypeCheck::Assignment"); - if (thrower == null) - throw ExceptionHandling.dieInternal(tc, - "Type check failed in assignment to '" + name + "'"); - else - Ops.invokeDirect(tc, thrower, - storeThrower, new Object[] { name, value, of }); - } - } public void store(ThreadContext tc, SixModelObject cont, SixModelObject value) { - checkStore(tc, cont, value); - RakOps.GlobalExt gcx = RakOps.key.getGC(tc); - SixModelObject whence = cont.get_attribute_boxed(tc, gcx.Scalar, "$!whence", HINT_whence); - if (whence != null) - Ops.invokeDirect(tc, whence, - WHENCE, new Object[] { }); - if (value.st.WHAT == gcx.Nil) { - SixModelObject desc = cont.get_attribute_boxed(tc, gcx.Scalar, - "$!descriptor", HINT_descriptor); - value = desc.get_attribute_boxed(tc, - gcx.ContainerDescriptor, "$!default", RakOps.HINT_CD_DEFAULT); - } - cont.bind_attribute_boxed(tc, gcx.Scalar, "$!value", HINT_value, value); + Ops.invokeDirect(tc, store, STORE, new Object[] { cont, value }); } public void store_i(ThreadContext tc, SixModelObject cont, long value) { store(tc, cont, RakOps.p6box_i(value, tc)); @@ -101,14 +54,8 @@ public void store_s(ThreadContext tc, SixModelObject cont, String value) { /* Stores a value in a container, without any checking of it (this * assumes an optimizer or something else already did it). Used for * assignment. */ - public void storeUnchecked(ThreadContext tc, SixModelObject cont, SixModelObject obj) { - SixModelObject Scalar = RakOps.key.getGC(tc).Scalar; - SixModelObject whence = cont.get_attribute_boxed(tc, Scalar, "$!whence", HINT_whence); - if (whence != null) - Ops.invokeDirect(tc, whence, - WHENCE, new Object[] { }); - - cont.bind_attribute_boxed(tc, Scalar, "$!value", HINT_value, obj); + public void storeUnchecked(ThreadContext tc, SixModelObject cont, SixModelObject value) { + Ops.invokeDirect(tc, storeUnchecked, STORE, new Object[] { cont, value }); } /* Not all containers are rw (ContainerSpec.canStore() defaults to true). */ @@ -116,12 +63,7 @@ public boolean canStore(ThreadContext tc, SixModelObject cont) { if (!(cont instanceof TypeObject)) { SixModelObject desc = cont.get_attribute_boxed(tc, cont.st.WHAT, "$!descriptor", HINT_descriptor); - if (desc != null) { - RakOps.GlobalExt gcx = RakOps.key.getGC(tc); - desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!rw", - RakOps.HINT_CD_RW); - return tc.native_i != 0; - } + return desc != null; } return false; } @@ -133,12 +75,18 @@ public String name() { /* Serializes the container data, if any. */ public void serialize(ThreadContext tc, STable st, SerializationWriter writer) { - /* No data to serialize. */ + writer.writeRef(store); + writer.writeRef(storeUnchecked); + writer.writeRef(cas); + writer.writeRef(atomicStore); } /* Deserializes the container data, if any. */ public void deserialize(ThreadContext tc, STable st, SerializationReader reader) { - /* No data to deserialize. */ + store = reader.readRef(); + storeUnchecked = reader.readRef(); + cas = reader.readRef(); + atomicStore = reader.readRef(); } /* Atomic operations. */ @@ -164,11 +112,8 @@ private void ensureAtomicsReady(SixModelObject cont) { public SixModelObject cas(ThreadContext tc, SixModelObject cont, SixModelObject expected, SixModelObject value) { - ensureAtomicsReady(cont); - checkStore(tc, cont, value); - return unsafe.compareAndSwapObject(cont, scalarValueOffset, expected, value) - ? expected - : (SixModelObject)unsafe.getObjectVolatile(cont, scalarValueOffset); + Ops.invokeDirect(tc, cas, CAS, new Object[] { cont, expected, value }); + return Ops.result_o(tc.curFrame); } public SixModelObject atomic_load(ThreadContext tc, SixModelObject cont) { @@ -178,8 +123,6 @@ public SixModelObject atomic_load(ThreadContext tc, SixModelObject cont) { public void atomic_store(ThreadContext tc, SixModelObject cont, SixModelObject value) { - ensureAtomicsReady(cont); - checkStore(tc, cont, value); - unsafe.putObjectVolatile(cont, scalarValueOffset, cont); + Ops.invokeDirect(tc, atomicStore, STORE, new Object[] { cont, value }); } } diff --git a/src/vm/moar/Perl6/Ops.nqp b/src/vm/moar/Perl6/Ops.nqp index 7bba19bbc7f..1115f82c172 100644 --- a/src/vm/moar/Perl6/Ops.nqp +++ b/src/vm/moar/Perl6/Ops.nqp @@ -41,21 +41,9 @@ MAST::ExtOpRegistry.register_extop('p6box_u', MAST::ExtOpRegistry.register_extop('p6bool', $MVM_operand_obj +| $MVM_operand_write_reg, $MVM_operand_int64 +| $MVM_operand_read_reg); -MAST::ExtOpRegistry.register_extop('p6scalarfromdesc', - $MVM_operand_obj +| $MVM_operand_write_reg, - $MVM_operand_obj +| $MVM_operand_read_reg); -MAST::ExtOpRegistry.register_extop('p6var', - $MVM_operand_obj +| $MVM_operand_write_reg, - $MVM_operand_obj +| $MVM_operand_read_reg); MAST::ExtOpRegistry.register_extop('p6reprname', $MVM_operand_obj +| $MVM_operand_write_reg, $MVM_operand_obj +| $MVM_operand_read_reg); -MAST::ExtOpRegistry.register_extop('p6recont_ro', - $MVM_operand_obj +| $MVM_operand_write_reg, - $MVM_operand_obj +| $MVM_operand_read_reg); -MAST::ExtOpRegistry.register_extop('p6decontrv', - $MVM_operand_obj +| $MVM_operand_write_reg, - $MVM_operand_obj +| $MVM_operand_read_reg); MAST::ExtOpRegistry.register_extop('p6capturelex', $MVM_operand_obj +| $MVM_operand_write_reg, $MVM_operand_obj +| $MVM_operand_read_reg); @@ -111,7 +99,6 @@ $ops.add_hll_moarop_mapping('perl6', 'p6box_i', 'p6box_i'); $ops.add_hll_moarop_mapping('perl6', 'p6box_n', 'p6box_n'); $ops.add_hll_moarop_mapping('perl6', 'p6box_s', 'p6box_s'); $ops.add_hll_moarop_mapping('perl6', 'p6box_u', 'p6box_u'); -$ops.add_hll_moarop_mapping('perl6', 'p6recont_ro', 'p6recont_ro'); $ops.add_hll_op('perl6', 'p6store', -> $qastcomp, $op { my @ops; my $cont_res := $qastcomp.as_mast($op[0], :want($MVM_reg_obj)); @@ -145,7 +132,6 @@ $ops.add_hll_op('perl6', 'p6store', -> $qastcomp, $op { MAST::InstructionList.new(@ops, $cont_res.result_reg, $MVM_reg_obj) }); -$ops.add_hll_moarop_mapping('perl6', 'p6var', 'p6var'); $ops.add_hll_moarop_mapping('perl6', 'p6reprname', 'p6reprname', :decont(0)); $ops.add_hll_op('perl6', 'p6definite', -> $qastcomp, $op { my @ops; @@ -318,7 +304,6 @@ my $p6bool := -> $qastcomp, $op { MAST::InstructionList.new(@ops, $res_reg, $MVM_reg_obj) }; $ops.add_hll_op('perl6', 'p6bool', $p6bool); -$ops.add_hll_moarop_mapping('perl6', 'p6scalarfromdesc', 'p6scalarfromdesc'); $ops.add_hll_op('perl6', 'p6invokehandler', -> $qastcomp, $op { $qastcomp.as_mast(QAST::Op.new( :op('call'), $op[0], $op[1] )); }); @@ -368,7 +353,6 @@ $ops.add_hll_op('perl6', 'p6sink', -> $qastcomp, $op { $ops.add_hll_op('nqp', 'p6bool', $p6bool); $ops.add_hll_moarop_mapping('nqp', 'p6init', 'p6init'); $ops.add_hll_moarop_mapping('nqp', 'p6settypes', 'p6settypes', 0); -$ops.add_hll_moarop_mapping('nqp', 'p6var', 'p6var'); $ops.add_hll_moarop_mapping('nqp', 'p6reprname', 'p6reprname'); $ops.add_hll_moarop_mapping('nqp', 'p6inpre', 'p6inpre'); $ops.add_hll_moarop_mapping('nqp', 'p6capturelexwhere', 'p6capturelexwhere'); @@ -659,12 +643,7 @@ $ops.add_hll_op('perl6', 'p6decontrv', -> $qastcomp, $op { else { $qastcomp.as_mast($op[1], :want($MVM_reg_str)) } } else { - my @ops; - my $value_res := $qastcomp.as_mast($op[1], :want($MVM_reg_obj), :want-decont); - push_ilist(@ops, $value_res); - nqp::push(@ops, MAST::ExtOp.new( :op('p6decontrv'), :cu($qastcomp.mast_compunit), - $value_res.result_reg, $value_res.result_reg )); - MAST::InstructionList.new(@ops, $value_res.result_reg, $MVM_reg_obj) + $qastcomp.as_mast(QAST::Op.new( :op('p6decontrv_internal'), $op[1] )); } } }); diff --git a/src/vm/moar/ops/container.c b/src/vm/moar/ops/container.c index e0a4bfb6454..f5f1a5766f0 100644 --- a/src/vm/moar/ops/container.c +++ b/src/vm/moar/ops/container.c @@ -2,14 +2,13 @@ #include "moar.h" #include "container.h" -/* Dummy, no-arg callsite. */ -static MVMCallsite no_arg_callsite = { NULL, 0, 0, 0 }; - -/* Dummy callsite for type_check. */ -static MVMCallsiteEntry tc_flags[] = { MVM_CALLSITE_ARG_OBJ, - MVM_CALLSITE_ARG_OBJ, - MVM_CALLSITE_ARG_OBJ }; -static MVMCallsite tc_callsite = { tc_flags, 3, 3, 3, 0 }; +/* Registered container operation callbacks. */ +typedef struct { + MVMObject *store; + MVMObject *store_unchecked; + MVMObject *cas; + MVMObject *atomic_store; +} RakudoContData; static void rakudo_scalar_fetch(MVMThreadContext *tc, MVMObject *cont, MVMRegister *res) { MVMObject *value = ((Rakudo_Scalar *)cont)->value; @@ -28,165 +27,14 @@ static void rakudo_scalar_fetch_s(MVMThreadContext *tc, MVMObject *cont, MVMRegi res->s = MVM_repr_get_str(tc, ((Rakudo_Scalar *)cont)->value); } -MVMObject * get_nil(); -MVMObject * get_mu(); - -static void finish_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - MVMObject *whence; - - /* Store the value. */ - MVM_ASSIGN_REF(tc, &(cont->header), rs->value, obj); - - /* Run any whence closure. */ - whence = rs->whence; - if (whence && IS_CONCRETE(whence)) { - MVMObject *code = MVM_frame_find_invokee(tc, whence, NULL); - MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &no_arg_callsite); - rs->whence = NULL; - STABLE(code)->invoke(tc, code, &no_arg_callsite, tc->cur_frame->args); - } -} - -typedef struct { - MVMObject *cont; - MVMObject *obj; - MVMRegister res; -} type_check_data; -void Rakudo_assign_typecheck_failed(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj); -static void type_check_ret(MVMThreadContext *tc, void *sr_data) { - type_check_data *tcd = (type_check_data *)sr_data; - MVMObject *cont = tcd->cont; - MVMObject *obj = tcd->obj; - MVMint64 res = tcd->res.i64; - free(tcd); - if (res) - finish_store(tc, cont, obj); - else - Rakudo_assign_typecheck_failed(tc, cont, obj); -} -static void mark_type_check_ret_data(MVMThreadContext *tc, MVMFrame *frame, - MVMGCWorklist *worklist) { - type_check_data *tcd = (type_check_data *)frame->extra->special_return_data; - MVM_gc_worklist_add(tc, worklist, &tcd->cont); - MVM_gc_worklist_add(tc, worklist, &tcd->obj); -} - -static void ensure_assignable(MVMThreadContext *tc, Rakudo_ContainerDescriptor *rcd) { - MVMint64 rw = 0; - if (rcd && IS_CONCRETE(rcd)) - rw = rcd->rw; - if (!rw) { - if (rcd && IS_CONCRETE(rcd) && rcd->name) { - char *c_name = MVM_string_utf8_encode_C_string(tc, rcd->name); - char *waste[] = { c_name, NULL }; - MVM_exception_throw_adhoc_free(tc, waste, - "Cannot assign to a readonly variable (%s) or a value", c_name); - } - else { - MVM_exception_throw_adhoc(tc, "Cannot assign to a readonly variable or a value"); - } - } -} - -static MVMint32 type_check_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj, - Rakudo_ContainerDescriptor *rcd, MVMSpecialReturn callback) { - /* Check against the type-check cache first (common, fast-path - * case). */ - MVMint64 mode = STABLE(rcd->of)->mode_flags & MVM_TYPE_CHECK_CACHE_FLAG_MASK; - if (rcd->of != get_mu() && !MVM_6model_istype_cache_only(tc, obj, rcd->of)) { - /* Failed. If the cache is definitive, we certainly have an error. */ - if (STABLE(obj)->type_check_cache && - (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 && - (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) { - Rakudo_assign_typecheck_failed(tc, cont, obj); - return 1; - } - - /* If we get here, need to call .^type_check on the value we're - * checking, unless it's an accepts check. */ - if (!STABLE(obj)->type_check_cache || (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD)) { - MVMObject *HOW, *meth; - MVMROOT(tc, cont, { - MVMROOT(tc, obj, { - MVMROOT(tc, rcd, { - HOW = MVM_6model_get_how_obj(tc, rcd->of); - MVMROOT(tc, HOW, { - meth = MVM_6model_find_method_cache_only(tc, HOW, - tc->instance->str_consts.type_check); - }); - }); - }); - }); - if (meth) { - /* Set up the call, using a fake register in special return - * data as the target. */ - MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); - type_check_data *tcd = malloc(sizeof(type_check_data)); - tcd->cont = cont; - tcd->obj = obj; - tcd->res.i64 = 0; - MVM_args_setup_thunk(tc, &tcd->res, MVM_RETURN_INT, &tc_callsite); - MVM_frame_special_return(tc, tc->cur_frame, callback, NULL, - tcd, mark_type_check_ret_data); - tc->cur_frame->args[0].o = HOW; - tc->cur_frame->args[1].o = obj; - tc->cur_frame->args[2].o = rcd->of; - STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); - return 1; - } - } - - /* If the flag to call .accepts_type on the target value is set, do so. */ - if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { - MVMObject *HOW, *meth; - MVMROOT(tc, cont, { - MVMROOT(tc, obj, { - MVMROOT(tc, rcd, { - HOW = MVM_6model_get_how_obj(tc, rcd->of); - MVMROOT(tc, HOW, { - meth = MVM_6model_find_method_cache_only(tc, HOW, - tc->instance->str_consts.accepts_type); - }); - }); - }); - }); - if (meth) { - /* Set up the call, using the result register as the target. */ - MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); - type_check_data *tcd = malloc(sizeof(type_check_data)); - tcd->cont = cont; - tcd->obj = obj; - tcd->res.i64 = 0; - MVM_args_setup_thunk(tc, &tcd->res, MVM_RETURN_INT, &tc_callsite); - MVM_frame_special_return(tc, tc->cur_frame, callback, NULL, - tcd, mark_type_check_ret_data); - tc->cur_frame->args[0].o = HOW; - tc->cur_frame->args[1].o = rcd->of; - tc->cur_frame->args[2].o = obj; - STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); - return 1; - } - else { - MVM_exception_throw_adhoc(tc, - "Expected 'accepts_type' method, but none found in meta-object"); - } - } - } - - return 0; -} - -static void rakudo_scalar_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - Rakudo_ContainerDescriptor *rcd = (Rakudo_ContainerDescriptor *)rs->descriptor; - ensure_assignable(tc, rcd); - if (!obj) - MVM_exception_throw_adhoc(tc, "Cannot assign a null value to a Perl 6 scalar"); - if (STABLE(obj)->WHAT == get_nil()) - obj = rcd->the_default; - if (!type_check_store(tc, cont, obj, rcd, type_check_ret)) - finish_store(tc, cont, obj); /* Didn't invoke, so complete store. */ +static void rakudo_scalar_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *value) { + RakudoContData *data = (RakudoContData *)STABLE(cont)->container_data; + MVMObject *code = MVM_frame_find_invokee(tc, data->store, NULL); + MVMCallsite *cs = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); + MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, cs); + tc->cur_frame->args[0].o = cont; + tc->cur_frame->args[1].o = value; + STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); } static void rakudo_scalar_store_i(MVMThreadContext *tc, MVMObject *cont, MVMint64 value) { @@ -213,16 +61,42 @@ static void rakudo_scalar_store_s(MVMThreadContext *tc, MVMObject *cont, MVMStri rakudo_scalar_store(tc, cont, boxed); } -static void rakudo_scalar_store_unchecked(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { - finish_store(tc, cont, obj); +static void rakudo_scalar_store_unchecked(MVMThreadContext *tc, MVMObject *cont, MVMObject *value) { + RakudoContData *data = (RakudoContData *)STABLE(cont)->container_data; + MVMObject *code = MVM_frame_find_invokee(tc, data->store_unchecked, NULL); + MVMCallsite *cs = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); + MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, cs); + tc->cur_frame->args[0].o = cont; + tc->cur_frame->args[1].o = value; + STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); +} + +static void rakudo_scalar_gc_mark_data(MVMThreadContext *tc, MVMSTable *st, MVMGCWorklist *worklist) { + RakudoContData *data = (RakudoContData *)st->container_data; + MVM_gc_worklist_add(tc, worklist, &data->store); + MVM_gc_worklist_add(tc, worklist, &data->store_unchecked); + MVM_gc_worklist_add(tc, worklist, &data->cas); + MVM_gc_worklist_add(tc, worklist, &data->atomic_store); +} + +static void rakudo_scalar_gc_free_data(MVMThreadContext *tc, MVMSTable *st) { + MVM_free_null(st->container_data); } static void rakudo_scalar_serialize(MVMThreadContext *tc, MVMSTable *st, MVMSerializationWriter *writer) { - /* Nothing to do. */ + RakudoContData *data = (RakudoContData *)st->container_data; + MVM_serialization_write_ref(tc, writer, data->store); + MVM_serialization_write_ref(tc, writer, data->store_unchecked); + MVM_serialization_write_ref(tc, writer, data->cas); + MVM_serialization_write_ref(tc, writer, data->atomic_store); } static void rakudo_scalar_deserialize(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { - /* Nothing to do. */ + RakudoContData *data = (RakudoContData *)st->container_data; + MVM_ASSIGN_REF(tc, &(st->header), data->store, MVM_serialization_read_ref(tc, reader)); + MVM_ASSIGN_REF(tc, &(st->header), data->store_unchecked, MVM_serialization_read_ref(tc, reader)); + MVM_ASSIGN_REF(tc, &(st->header), data->cas, MVM_serialization_read_ref(tc, reader)); + MVM_ASSIGN_REF(tc, &(st->header), data->atomic_store, MVM_serialization_read_ref(tc, reader)); } static void rakudo_scalar_spesh(MVMThreadContext *tc, MVMSTable *st, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { @@ -242,156 +116,20 @@ static void rakudo_scalar_spesh(MVMThreadContext *tc, MVMSTable *st, MVMSpeshGra static MVMint32 rakudo_scalar_can_store(MVMThreadContext *tc, MVMObject *cont) { Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - Rakudo_ContainerDescriptor *rcd = (Rakudo_ContainerDescriptor *)rs->descriptor; - return rcd && IS_CONCRETE(rcd) && rcd->rw; -} - -static void finish_cas(MVMThreadContext *tc, MVMObject *cont, MVMObject *expected, - MVMObject *value, MVMRegister *result) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - result->o = (MVMObject *)MVM_casptr(&(rs->value), expected, value); - MVM_gc_write_barrier(tc, (MVMCollectable *)cont, (MVMCollectable *)value); -} - -typedef struct { - MVMObject *cont; - MVMObject *expected; - MVMObject *value; - MVMRegister *cas_result; - MVMRegister res; -} cas_type_check_data; -static void cas_type_check_ret(MVMThreadContext *tc, void *sr_data) { - cas_type_check_data *tcd = (cas_type_check_data *)sr_data; - MVMObject *cont = tcd->cont; - MVMObject *expected = tcd->expected; - MVMObject *value = tcd->value; - MVMRegister *cas_result = tcd->cas_result; - MVMint64 res = tcd->res.i64; - free(tcd); - if (res) - finish_cas(tc, cont, expected, value, cas_result); - else - Rakudo_assign_typecheck_failed(tc, cont, value); -} -static void mark_cas_type_check_ret_data(MVMThreadContext *tc, MVMFrame *frame, - MVMGCWorklist *worklist) { - cas_type_check_data *tcd = (cas_type_check_data *)frame->extra->special_return_data; - MVM_gc_worklist_add(tc, worklist, &tcd->cont); - MVM_gc_worklist_add(tc, worklist, &tcd->expected); - MVM_gc_worklist_add(tc, worklist, &tcd->value); + return !MVM_is_null(tc, rs->descriptor); } static void rakudo_scalar_cas(MVMThreadContext *tc, MVMObject *cont, MVMObject *expected, MVMObject *value, MVMRegister *result) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - Rakudo_ContainerDescriptor *rcd = (Rakudo_ContainerDescriptor *)rs->descriptor; - ensure_assignable(tc, rcd); - - /* Handle Nil and type-checking. */ - if (!value) { - MVM_exception_throw_adhoc(tc, "Cannot cas a null value into a Perl 6 scalar"); - } - else { - MVMint64 mode; - if (STABLE(value)->WHAT == get_nil()) { - value = rcd->the_default; - } - - /* Check against the type-check cache first (common, fast-path - * case). */ - mode = STABLE(rcd->of)->mode_flags & MVM_TYPE_CHECK_CACHE_FLAG_MASK; - if (rcd->of != get_mu() && !MVM_6model_istype_cache_only(tc, value, rcd->of)) { - /* Failed. If the cache is definitive, we certainly have an error. */ - if (STABLE(value)->type_check_cache && - (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 && - (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) { - Rakudo_assign_typecheck_failed(tc, cont, value); - return; - } - - /* If we get here, need to call .^type_check on the value we're - * checking, unless it's an accepts check. */ - if (!STABLE(value)->type_check_cache || (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD)) { - MVMObject *HOW, *meth; - MVMROOT(tc, cont, { - MVMROOT(tc, expected, { - MVMROOT(tc, value, { - MVMROOT(tc, rcd, { - HOW = MVM_6model_get_how_obj(tc, rcd->of); - MVMROOT(tc, HOW, { - meth = MVM_6model_find_method_cache_only(tc, HOW, - tc->instance->str_consts.type_check); - }); - }); - }); - }); - }); - if (meth) { - /* Set up the call, using a fake register in special return - * data as the target. */ - MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); - cas_type_check_data *tcd = malloc(sizeof(cas_type_check_data)); - tcd->cont = cont; - tcd->expected = expected; - tcd->value = value; - tcd->cas_result = result; - tcd->res.i64 = 0; - MVM_args_setup_thunk(tc, &tcd->res, MVM_RETURN_INT, &tc_callsite); - MVM_frame_special_return(tc, tc->cur_frame, cas_type_check_ret, NULL, - tcd, mark_cas_type_check_ret_data); - tc->cur_frame->args[0].o = HOW; - tc->cur_frame->args[1].o = value; - tc->cur_frame->args[2].o = rcd->of; - STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); - return; - } - } - - /* If the flag to call .accepts_type on the target value is set, do so. */ - if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { - MVMObject *HOW, *meth; - MVMROOT(tc, cont, { - MVMROOT(tc, expected, { - MVMROOT(tc, value, { - MVMROOT(tc, rcd, { - HOW = MVM_6model_get_how_obj(tc, rcd->of); - MVMROOT(tc, HOW, { - meth = MVM_6model_find_method_cache_only(tc, HOW, - tc->instance->str_consts.accepts_type); - }); - }); - }); - }); - }); - if (meth) { - /* Set up the call, using the result register as the target. */ - MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); - cas_type_check_data *tcd = malloc(sizeof(cas_type_check_data)); - tcd->cont = cont; - tcd->expected = expected; - tcd->value = value; - tcd->cas_result = result; - tcd->res.i64 = 0; - MVM_args_setup_thunk(tc, &tcd->res, MVM_RETURN_INT, &tc_callsite); - MVM_frame_special_return(tc, tc->cur_frame, cas_type_check_ret, NULL, - tcd, mark_cas_type_check_ret_data); - tc->cur_frame->args[0].o = HOW; - tc->cur_frame->args[1].o = rcd->of; - tc->cur_frame->args[2].o = value; - STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); - return; - } - else { - MVM_exception_throw_adhoc(tc, - "Expected 'accepts_type' method, but none found in meta-object"); - } - } - } - } - - /* Type check passed without needing invocation; finish the CAS. */ - finish_cas(tc, cont, expected, value, result); + RakudoContData *data = (RakudoContData *)STABLE(cont)->container_data; + MVMObject *code = MVM_frame_find_invokee(tc, data->cas, NULL); + MVMCallsite *cs = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TYPECHECK); + MVM_args_setup_thunk(tc, result, MVM_RETURN_OBJ, cs); + tc->cur_frame->args[0].o = cont; + tc->cur_frame->args[1].o = expected; + tc->cur_frame->args[2].o = value; + STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); } static MVMObject * rakudo_scalar_atomic_load(MVMThreadContext *tc, MVMObject *cont) { @@ -399,34 +137,14 @@ static MVMObject * rakudo_scalar_atomic_load(MVMThreadContext *tc, MVMObject *co return value ? value : tc->instance->VMNull; } -static void finish_atomic_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - MVM_store(&(rs->value), obj); - MVM_gc_write_barrier(tc, (MVMCollectable *)cont, (MVMCollectable *)obj); -} - -static void atomic_store_type_check_ret(MVMThreadContext *tc, void *sr_data) { - type_check_data *tcd = (type_check_data *)sr_data; - MVMObject *cont = tcd->cont; - MVMObject *obj = tcd->obj; - MVMint64 res = tcd->res.i64; - free(tcd); - if (res) - finish_atomic_store(tc, cont, obj); - else - Rakudo_assign_typecheck_failed(tc, cont, obj); -} - void rakudo_scalar_atomic_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *value) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - Rakudo_ContainerDescriptor *rcd = (Rakudo_ContainerDescriptor *)rs->descriptor; - ensure_assignable(tc, rcd); - if (!value) - MVM_exception_throw_adhoc(tc, "Cannot assign a null value to a Perl 6 scalar"); - if (STABLE(value)->WHAT == get_nil()) - value = rcd->the_default; - if (!type_check_store(tc, cont, value, rcd, atomic_store_type_check_ret)) - finish_atomic_store(tc, cont, value); /* Type check didn't invoke. */ + RakudoContData *data = (RakudoContData *)STABLE(cont)->container_data; + MVMObject *code = MVM_frame_find_invokee(tc, data->atomic_store, NULL); + MVMCallsite *cs = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); + MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, cs); + tc->cur_frame->args[0].o = cont; + tc->cur_frame->args[1].o = value; + STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); } static const MVMContainerSpec rakudo_scalar_spec = { @@ -441,8 +159,8 @@ static const MVMContainerSpec rakudo_scalar_spec = { rakudo_scalar_store_s, rakudo_scalar_store_unchecked, rakudo_scalar_spesh, - NULL, - NULL, + rakudo_scalar_gc_mark_data, + rakudo_scalar_gc_free_data, rakudo_scalar_serialize, rakudo_scalar_deserialize, rakudo_scalar_can_store, @@ -453,11 +171,33 @@ static const MVMContainerSpec rakudo_scalar_spec = { }; static void rakudo_scalar_set_container_spec(MVMThreadContext *tc, MVMSTable *st) { + RakudoContData *data = MVM_calloc(1, sizeof(RakudoContData)); + st->container_data = data; st->container_spec = &rakudo_scalar_spec; } +static MVMObject * grab_one_value(MVMThreadContext *tc, MVMObject *config, const char *key) { + MVMString *key_str; + MVMROOT(tc, config, { + key_str = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, key); + }); + if (!MVM_repr_exists_key(tc, config, key_str)) + MVM_exception_throw_adhoc(tc, "Container spec must be configured with a '%s'", key); + return MVM_repr_at_key_o(tc, config, key_str); +} static void rakudo_scalar_configure_container_spec(MVMThreadContext *tc, MVMSTable *st, MVMObject *config) { - /* Nothing to do. */ + RakudoContData *data = (RakudoContData *)st->container_data; + MVMROOT2(tc, st, config, { + MVMObject *value; + value = grab_one_value(tc, config, "store"); + MVM_ASSIGN_REF(tc, &(st->header), data->store, value); + value = grab_one_value(tc, config, "store_unchecked"); + MVM_ASSIGN_REF(tc, &(st->header), data->store_unchecked, value); + value = grab_one_value(tc, config, "cas"); + MVM_ASSIGN_REF(tc, &(st->header), data->cas, value); + value = grab_one_value(tc, config, "atomic_store"); + MVM_ASSIGN_REF(tc, &(st->header), data->atomic_store, value); + }); } static const MVMContainerConfigurer ContainerConfigurer = { diff --git a/src/vm/moar/ops/container.h b/src/vm/moar/ops/container.h index 69a1b7ad94f..d43aeb4ae6d 100644 --- a/src/vm/moar/ops/container.h +++ b/src/vm/moar/ops/container.h @@ -1,19 +1,8 @@ -/* The ContainerDescriptor class. Depends on P6opaque object layout. */ -typedef struct { - MVMP6opaque p6o; - MVMObject *of; /* Type of value. */ - MVMint64 rw; /* Non-zero if we can write. */ - MVMString *name; /* The name of the container, if any. */ - MVMObject *the_default; /* The default value if any. */ - MVMint64 is_dynamic; /* The container is dynamically visible */ -} Rakudo_ContainerDescriptor; - /* The Scalar class. Depends on P6opaque object layout. */ typedef struct { MVMP6opaque p6o; MVMObject *descriptor; /* Container descriptor. */ MVMObject *value; /* The currently held value. */ - MVMObject *whence; /* Any whence property */ } Rakudo_Scalar; void Rakudo_containers_setup(MVMThreadContext *tc); diff --git a/src/vm/moar/ops/perl6_ops.c b/src/vm/moar/ops/perl6_ops.c index 5b1d236f58e..ea307305866 100644 --- a/src/vm/moar/ops/perl6_ops.c +++ b/src/vm/moar/ops/perl6_ops.c @@ -47,28 +47,15 @@ static MVMObject *Any = NULL; static MVMObject *Int = NULL; static MVMObject *Num = NULL; static MVMObject *Str = NULL; -static MVMObject *Scalar = NULL; static MVMObject *True = NULL; static MVMObject *False = NULL; -static MVMObject *ContainerDescriptor = NULL; -static MVMObject *Nil = NULL; - -/* Default container descriptor. */ -static MVMObject *default_cont_desc = NULL; /* Useful string constants. */ -static MVMString *str_return = NULL; static MVMString *str_dispatcher = NULL; static MVMString *str_vivify_for = NULL; static MVMString *str_perl6 = NULL; static MVMString *str_p6ex = NULL; static MVMString *str_xnodisp = NULL; -static MVMString *str_xatcf = NULL; -static MVMString *str_cfr = NULL; - -/* Expose Nil and Mu for containers. */ -MVMObject * get_nil() { return Nil; } -MVMObject * get_mu() { return Mu; } /* Looks up an exception thrower. */ static MVMObject * get_thrower(MVMThreadContext *tc, MVMString *type) { @@ -76,24 +63,6 @@ static MVMObject * get_thrower(MVMThreadContext *tc, MVMString *type) { return MVM_is_null(tc, ex_hash) ? ex_hash : MVM_repr_at_key_o(tc, ex_hash, type); } -/* Reports an assignment type check failure. */ -void Rakudo_assign_typecheck_failed(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { - MVMObject *thrower = get_thrower(tc, str_xatcf); - if (!MVM_is_null(tc, thrower)) { - Rakudo_Scalar *rs = (Rakudo_Scalar *)cont; - Rakudo_ContainerDescriptor *rcd = (Rakudo_ContainerDescriptor *)rs->descriptor; - thrower = MVM_frame_find_invokee(tc, thrower, NULL); - MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &atcf_callsite); - tc->cur_frame->args[0].s = rcd->name; - tc->cur_frame->args[1].o = obj; - tc->cur_frame->args[2].o = rcd->of; - STABLE(thrower)->invoke(tc, thrower, &atcf_callsite, tc->cur_frame->args); - } - else { - MVM_exception_throw_adhoc(tc, "Type check failed in assignment"); - } -} - /* Initializes the Perl 6 extension ops. */ static void p6init(MVMThreadContext *tc, MVMuint8 *cur_op) { if (!initialized) { @@ -118,31 +87,11 @@ static void p6settypes(MVMThreadContext *tc, MVMuint8 *cur_op) { get_type(tc, conf, "Int", Int); get_type(tc, conf, "Num", Num); get_type(tc, conf, "Str", Str); - get_type(tc, conf, "Scalar", Scalar); get_type(tc, conf, "True", True); get_type(tc, conf, "False", False); - get_type(tc, conf, "ContainerDescriptor", ContainerDescriptor); - get_type(tc, conf, "Nil", Nil); }); - /* Set up default container descriptor. */ - { - MVMString *element; - default_cont_desc = MVM_repr_alloc_init(tc, ContainerDescriptor); - MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&default_cont_desc, "DefaultContainerDescriptor"); - element = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, ""); - MVM_ASSIGN_REF(tc, &(default_cont_desc->header), - ((Rakudo_ContainerDescriptor *)default_cont_desc)->of, Mu); - MVM_ASSIGN_REF(tc, &(default_cont_desc->header), - ((Rakudo_ContainerDescriptor *)default_cont_desc)->name, element); - ((Rakudo_ContainerDescriptor *)default_cont_desc)->rw = 1; - MVM_ASSIGN_REF(tc, &(default_cont_desc->header), - ((Rakudo_ContainerDescriptor *)default_cont_desc)->the_default, Any); - } - /* Strings. */ - str_return = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "RETURN"); - MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_return, "RETURN"); str_dispatcher = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "$*DISPATCHER"); MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_dispatcher, "$*DISPATCHER"); str_vivify_for = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "vivify_for"); @@ -153,10 +102,6 @@ static void p6settypes(MVMThreadContext *tc, MVMuint8 *cur_op) { MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_p6ex, "P6EX"); str_xnodisp = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "X::NoDispatcher"); MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_xnodisp, "X::NoDispatcher"); - str_xatcf = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "X::TypeCheck::Assignment"); - MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_xatcf, "X::TypeCheck::Assignment"); - str_cfr = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "X::ControlFlow::Return"); - MVM_gc_root_add_permanent_desc(tc, (MVMCollectable **)&str_cfr, "X::ControlFlow::Return"); } /* Boxing to Perl 6 types. */ @@ -235,70 +180,6 @@ static void p6bool_discover(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns #endif } -/* Creates a Scalar from the specified descriptor. */ -static MVMuint8 s_p6scalarfromdesc[] = { - MVM_operand_obj | MVM_operand_write_reg, - MVM_operand_obj | MVM_operand_read_reg, -}; -static void p6scalarfromdesc(MVMThreadContext *tc, MVMuint8 *cur_op) { - MVMObject *new_scalar = MVM_repr_alloc_init(tc, Scalar); - MVMObject *descriptor = GET_REG(tc, 2).o; - if (MVM_is_null(tc, descriptor) || !IS_CONCRETE(descriptor)) { - descriptor = default_cont_desc; - } - MVM_ASSIGN_REF(tc, &(new_scalar->header), ((Rakudo_Scalar *)new_scalar)->descriptor, descriptor); - MVM_ASSIGN_REF(tc, &(new_scalar->header), ((Rakudo_Scalar *)new_scalar)->value, - ((Rakudo_ContainerDescriptor *)descriptor)->the_default); - GET_REG(tc, 0).o = new_scalar; -} -static void p6scalarfromdesc_discover(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { - MVMSpeshFacts *tfacts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]); - tfacts->flags |= MVM_SPESH_FACT_CONCRETE | MVM_SPESH_FACT_KNOWN_TYPE; - tfacts->type = Scalar; -} - -static MVMuint8 s_p6recont_ro[] = { - MVM_operand_obj | MVM_operand_write_reg, - MVM_operand_obj | MVM_operand_read_reg, -}; -static void p6recont_ro(MVMThreadContext *tc, MVMuint8 *cur_op) { - MVMObject *check = GET_REG(tc, 2).o; - if (IS_CONCRETE(check) && STABLE(check)->container_spec == Rakudo_containers_get_scalar()) { - MVMObject *desc = ((Rakudo_Scalar *)check)->descriptor; - if (!MVM_is_null(tc, desc) && ((Rakudo_ContainerDescriptor *)desc)->rw) { - /* We have an rw container; re-containerize it. */ - MVMROOT(tc, check, { - MVMObject *result = MVM_repr_alloc_init(tc, Scalar); - MVM_ASSIGN_REF(tc, &(result->header), ((Rakudo_Scalar *)result)->value, - ((Rakudo_Scalar *)check)->value); - GET_REG(tc, 0).o = result; - }); - return; - } - } - GET_REG(tc, 0).o = check; -} - -/* The .VAR operation. Wraps in an outer Scalar container so we can actually - * operate on the underlying Scalar, if we have a container. */ -static MVMuint8 s_p6var[] = { - MVM_operand_obj | MVM_operand_write_reg, - MVM_operand_obj | MVM_operand_read_reg, -}; -static void p6var(MVMThreadContext *tc, MVMuint8 *cur_op) { - MVMObject *wrappee = GET_REG(tc, 2).o; - if (STABLE(wrappee)->container_spec) { - MVMROOT(tc, wrappee, { - MVMObject *wrapper = MVM_repr_alloc_init(tc, Scalar); - MVM_ASSIGN_REF(tc, &(wrapper->header), ((Rakudo_Scalar *)wrapper)->value, wrappee); - GET_REG(tc, 0).o = wrapper; - }); - } - else { - GET_REG(tc, 0).o = wrappee; - } -} - static MVMuint8 s_p6reprname[] = { MVM_operand_obj | MVM_operand_write_reg, MVM_operand_obj | MVM_operand_read_reg, @@ -319,61 +200,6 @@ static void p6reprname_discover(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpesh discover_create(tc, g, ins, tc->instance->boot_types.BOOTStr); } -/* Decontainerizes the return value of a routine as needed. */ -static MVMuint8 s_p6decontrv[] = { - MVM_operand_obj | MVM_operand_write_reg, - MVM_operand_obj | MVM_operand_read_reg, -}; -static MVMObject *Iterable = NULL; -static void p6decontrv(MVMThreadContext *tc, MVMuint8 *cur_op) { - MVMObject *retval; - if (!Iterable) - Iterable = MVM_frame_find_lexical_by_name(tc, - MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "Iterable"), - MVM_reg_obj)->o; - retval = GET_REG(tc, 2).o; - if (MVM_is_null(tc, retval)) { - retval = Mu; - } - else if (IS_CONCRETE(retval)) { - const MVMContainerSpec *spec = STABLE(retval)->container_spec; - if (spec == Rakudo_containers_get_scalar()) { - Rakudo_ContainerDescriptor *cd = (Rakudo_ContainerDescriptor *) - ((Rakudo_Scalar *)retval)->descriptor; - if (!MVM_is_null(tc, (MVMObject *)cd) && cd->rw) { - MVMObject *value = ((Rakudo_Scalar *)retval)->value; - if (MVM_6model_istype_cache_only(tc, value, Iterable)) { - MVMROOT(tc, value, { - MVMObject *cont = MVM_repr_alloc_init(tc, Scalar); - MVM_ASSIGN_REF(tc, &(cont->header), ((Rakudo_Scalar *)cont)->value, - value); - retval = cont; - }); - } - else { - retval = value; - } - } - } - else if (spec && spec->fetch_never_invokes) { - MVMRegister res; - spec->fetch(tc, retval, &res); - retval = res.o; - } - } - GET_REG(tc, 0).o = retval; -} -static void p6decontrv_spesh(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { - /* If it's already deconted, can just become a set. */ - MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]); - if (obj_facts->flags & (MVM_SPESH_FACT_DECONTED | MVM_SPESH_FACT_TYPEOBJ)) { - MVMSpeshFacts *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); - ins->info = MVM_op_get_op(MVM_OP_set); - res_facts->flags = obj_facts->flags; - res_facts->type = obj_facts->type; - } -} - static MVMuint8 s_p6capturelex[] = { MVM_operand_obj | MVM_operand_write_reg, MVM_operand_obj | MVM_operand_read_reg, @@ -741,11 +567,7 @@ MVM_DLL_EXPORT void Rakudo_ops_init(MVMThreadContext *tc) { MVM_ext_register_extop(tc, "p6box_u", p6box_u, 2, s_p6box_u, NULL, p6box_u_discover, MVM_EXTOP_PURE | MVM_EXTOP_ALLOCATING); MVM_ext_register_extop(tc, "p6settypes", p6settypes, 1, s_p6settypes, NULL, NULL, 0); MVM_ext_register_extop(tc, "p6bool", p6bool, 2, s_p6bool, NULL, p6bool_discover, MVM_EXTOP_PURE); - MVM_ext_register_extop(tc, "p6scalarfromdesc", p6scalarfromdesc, 2, s_p6scalarfromdesc, NULL, p6scalarfromdesc_discover, MVM_EXTOP_PURE | MVM_EXTOP_ALLOCATING); - MVM_ext_register_extop(tc, "p6recont_ro", p6recont_ro, 2, s_p6recont_ro, NULL, NULL, MVM_EXTOP_PURE); - MVM_ext_register_extop(tc, "p6var", p6var, 2, s_p6var, NULL, NULL, MVM_EXTOP_PURE | MVM_EXTOP_ALLOCATING); MVM_ext_register_extop(tc, "p6reprname", p6reprname, 2, s_p6reprname, NULL, p6reprname_discover, MVM_EXTOP_PURE | MVM_EXTOP_ALLOCATING); - MVM_ext_register_extop(tc, "p6decontrv", p6decontrv, 2, s_p6decontrv, p6decontrv_spesh, NULL, MVM_EXTOP_PURE); MVM_ext_register_extop(tc, "p6capturelex", p6capturelex, 2, s_p6capturelex, NULL, NULL, 0); MVM_ext_register_extop(tc, "p6capturelexwhere", p6capturelexwhere, 2, s_p6capturelexwhere, NULL, NULL, 0); MVM_ext_register_extop(tc, "p6getouterctx", p6getouterctx, 2, s_p6getouterctx, NULL, NULL, MVM_EXTOP_PURE | MVM_EXTOP_ALLOCATING); diff --git a/src/vm/moar/spesh-plugins.nqp b/src/vm/moar/spesh-plugins.nqp index 912c9258785..a83d94cb273 100644 --- a/src/vm/moar/spesh-plugins.nqp +++ b/src/vm/moar/spesh-plugins.nqp @@ -43,3 +43,222 @@ nqp::speshreg('perl6', 'maybemeth', -> $obj, str $name { ?? $meth !! &discard-and-nil }); + +## Return value decontainerization plugin + +# Often we have nothing at all to do, in which case we can make it a no-op. +# Other times, we need a decont. In a few, we need to re-wrap it. + +{ + # We look up Iterable when the plugin is used. + my $Iterable := nqp::null(); + + sub identity($obj) { $obj } + sub decont($obj) { nqp::decont($obj) } + sub recont($obj) { + my $rc := nqp::create(Scalar); + nqp::bindattr($rc, Scalar, '$!value', nqp::decont($obj)); + $rc + } + sub decontrv($cont) { + if nqp::isrwcont($cont) { + # It's an RW container, so we really need to decont it. + my $rv := nqp::decont($cont); + if nqp::istype($rv, $Iterable) { + my $rc := nqp::create(Scalar); + nqp::bindattr($rc, Scalar, '$!value', $rv); + $rc + } + else { + $rv + } + } + else { + # A read-only container, so just return it. + $cont + } + } + + nqp::speshreg('perl6', 'decontrv', sub ($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); + + # If it's a Scalar container then we can optimize further. + if nqp::eqaddr(nqp::what_nd($rv), Scalar) { + # Grab the descriptor. + my $desc := nqp::speshguardgetattr($rv, Scalar, '$!descriptor'); + if nqp::isconcrete($desc) { + # Descriptor, so `rw`. Guard on type of value. If it's + # Iterable, re-containerize. If not, just decont. + nqp::speshguardconcrete($desc); + my $value := nqp::speshguardgetattr($rv, Scalar, '$!value'); + nqp::speshguardtype($value, nqp::what_nd($value)); + return nqp::istype($value, $Iterable) ?? &recont !! &decont; + } + else { + # No descriptor, so it's already readonly. Identity. + nqp::speshguardtypeobj($desc); + return &identity; + } + } + + # Otherwise, full decont. + return &decontrv; + } + else { + # No decontainerization to do, so just produce identity. + unless nqp::isconcrete($rv) { + # Needed as a container's type object is not a container, but a + # concrete instance would be. + nqp::speshguardtypeobj($rv); + } + return &identity; + } + }); +} + +## Assignment plugin + +# We case-analyze assignments and provide these optimized paths for a range of +# common situations. +sub assign-type-error($desc, $value) { + my %x := nqp::gethllsym('perl6', 'P6EX'); + if nqp::ishash(%x) { + %x($desc.name, $value, $desc.of); + } + else { + nqp::die("Type check failed in assignment"); + } +} +sub assign-fallback($cont, $value) { + nqp::assign($cont, $value) +} +sub assign-scalar-no-whence-no-typecheck($cont, $value) { + nqp::bindattr($cont, Scalar, '$!value', $value); +} +sub assign-scalar-no-whence($cont, $value) { + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + my $type := nqp::getattr($desc, ContainerDescriptor, '$!of'); + if nqp::istype($value, $type) { + nqp::bindattr($cont, Scalar, '$!value', $value); + } + else { + assign-type-error($desc, $value); + } +} +sub assign-scalar-bindpos-no-typecheck($cont, $value) { + nqp::bindattr($cont, Scalar, '$!value', $value); + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + nqp::bindpos( + nqp::getattr($desc, ContainerDescriptor::BindArrayPos, '$!target'), + nqp::getattr_i($desc, ContainerDescriptor::BindArrayPos, '$!pos'), + $cont); + nqp::bindattr($cont, Scalar, '$!descriptor', + nqp::getattr($desc, ContainerDescriptor::BindArrayPos, '$!next-descriptor')); +} +sub assign-scalar-bindpos($cont, $value) { + my $desc := nqp::getattr($cont, Scalar, '$!descriptor'); + my $next := nqp::getattr($desc, ContainerDescriptor::BindArrayPos, '$!next-descriptor'); + my $type := nqp::getattr($next, ContainerDescriptor, '$!of'); + if nqp::istype($value, $type) { + nqp::bindattr($cont, Scalar, '$!value', $value); + nqp::bindpos( + nqp::getattr($desc, ContainerDescriptor::BindArrayPos, '$!target'), + nqp::getattr_i($desc, ContainerDescriptor::BindArrayPos, '$!pos'), + $cont); + nqp::bindattr($cont, Scalar, '$!descriptor', $next); + } + else { + assign-type-error($next, $value); + } +} + +# Assignment to a $ sigil variable, usually Scalar. +nqp::speshreg('perl6', 'assign', sub ($cont, $value) { + # Whatever we do, we'll guard on the type of the container and its + # concreteness. + nqp::speshguardtype($cont, nqp::what_nd($cont)); + nqp::isconcrete_nd($cont) + ?? nqp::speshguardconcrete($cont) + !! nqp::speshguardtypeobj($cont); + + # We have various fast paths for an assignment to a Scalar. + if nqp::eqaddr(nqp::what_nd($cont), Scalar) && nqp::isconcrete_nd($cont) { + # Now see what the Scalar descriptor type is. + my $desc := nqp::speshguardgetattr($cont, Scalar, '$!descriptor'); + if nqp::eqaddr($desc.WHAT, ContainerDescriptor) && nqp::isconcrete($desc) { + # Simple assignment, no whence. But is Nil being assigned? + if nqp::eqaddr($value, Nil) { + # Yes; NYI. + } + else { + # No whence, no Nil. Is it a nominal type? If yes, we can check + # it here. There are two interesting cases. One is if the type + # constraint is Mu. To avoid a huge guard set at megamorphic + # assignment sites, for this case we just guard $!of being Mu + # and the value not being Nil. For other cases, where there is + # a type constraint, we guard on the descriptor and the value, + # provided it typechecks OK. + my $of := $desc.of; + unless $of.HOW.archetypes.nominal { + nqp::speshguardobj($desc); + return &assign-scalar-no-whence; + } + if nqp::eqaddr($of, Mu) { + nqp::speshguardtype($desc, $desc.WHAT); + nqp::speshguardconcrete($desc); + my $of := nqp::speshguardgetattr($desc, ContainerDescriptor, '$!of'); + nqp::speshguardobj($of); + nqp::speshguardnotobj($value, Nil); + return &assign-scalar-no-whence-no-typecheck; + } + elsif nqp::istype($value, $of) { + nqp::speshguardobj($desc); + nqp::speshguardtype($value, $value.WHAT); + return &assign-scalar-no-whence-no-typecheck; + } + else { + # Will fail the type check and error always. + return &assign-scalar-no-whence; + } + } + } + elsif nqp::eqaddr($desc.WHAT, ContainerDescriptor::BindArrayPos) { + # Bind into an array. We can produce a fast path for this, though + # should check what the ultimate descriptor is. It really should + # be a normal, boring, container descriptor. + nqp::speshguardtype($desc, $desc.WHAT); + nqp::speshguardconcrete($desc); + my $next := nqp::speshguardgetattr($desc, ContainerDescriptor::BindArrayPos, + '$!next-descriptor'); + if nqp::eqaddr($next.WHAT, ContainerDescriptor) { + # Ensure we're not assigning Nil. (This would be very odd, as + # a Scalar starts off with its default value, and if we are + # vivifying we'll likely have a new container). + unless nqp::eqaddr($value.WHAT, Nil) { + # Go by whether we can type check the target. + nqp::speshguardobj($next); + nqp::speshguardtype($value, $value.WHAT); + my $of := $next.of; + if $of.HOW.archetypes.nominal && + (nqp::eqaddr($of, Mu) || nqp::istype($value, $of)) { + return &assign-scalar-bindpos-no-typecheck; + } + else { + # No whence, not a Nil, but still need to type check + # (perhaps subset type, perhaps error). + return &assign-scalar-bindpos; + } + } + } + } + } + + # If we get here, then we didn't have a specialized case to put in + # place. + return &assign-fallback; +}); diff --git a/tools/build/Makefile-JVM.in b/tools/build/Makefile-JVM.in index 2a2496f229f..e8a1fabe4b7 100644 --- a/tools/build/Makefile-JVM.in +++ b/tools/build/Makefile-JVM.in @@ -13,8 +13,8 @@ J_LIBPATH = @nqp_libdir@ NQP_JARS = @nqp_jars@ BLD_NQP_JARS = @bld_nqp_jars@ -J_RUN_NQP_RR = $(JAVA) -Xss1m -Xms500m -Xmx2000m -cp .@cpsep@$(BLD_NQP_JARS)@cpsep@rakudo-runtime.jar@cpsep@$(SYSROOT)@nqp_classpath@ nqp -J_RUN_PERL6 = $(JAVA) -Xss1m -Xms500m -Xmx2000m -cp .@cpsep@$(BLD_NQP_JARS)@cpsep@rakudo-runtime.jar@cpsep@perl6.jar@cpsep@$(SYSROOT)@nqp_classpath@ perl6 +J_RUN_NQP_RR = $(JAVA) -Xss1m -Xms500m -Xmx3000m -cp .@cpsep@$(BLD_NQP_JARS)@cpsep@rakudo-runtime.jar@cpsep@$(SYSROOT)@nqp_classpath@ nqp +J_RUN_PERL6 = $(JAVA) -Xss1m -Xms500m -Xmx3000m -cp .@cpsep@$(BLD_NQP_JARS)@cpsep@rakudo-runtime.jar@cpsep@perl6.jar@cpsep@$(SYSROOT)@nqp_classpath@ perl6 RUNTIME_JAVAS = src/vm/jvm/runtime/org/perl6/rakudo/*.java diff --git a/tools/build/NQP_REVISION b/tools/build/NQP_REVISION index 253a6784aac..8e8a02796bc 100644 --- a/tools/build/NQP_REVISION +++ b/tools/build/NQP_REVISION @@ -1 +1 @@ -2018.06-27-gcec76ff79 +2018.06-41-gf8f7d34 diff --git a/tools/build/common_bootstrap_sources b/tools/build/common_bootstrap_sources index 47a98a60378..4ffff99a5e8 100644 --- a/tools/build/common_bootstrap_sources +++ b/tools/build/common_bootstrap_sources @@ -44,5 +44,4 @@ src/Perl6/Metamodel/SubsetHOW.nqp src/Perl6/Metamodel/EnumHOW.nqp src/Perl6/Metamodel/CoercionHOW.nqp src/Perl6/Metamodel/DefiniteHOW.nqp -src/Perl6/Metamodel/ContainerDescriptor.nqp src/Perl6/Metamodel/Dispatchers.nqp