Consider this Perl 6 code:
sub refinc($var) {
my $myvar = $var;
$myvar += 1;
}
If you pass an integer in here, it should do nothing. However, if you
pass an object with an overloaded += in, it should have the
side-effect of adding one to that object. It's just a value/reference
semantics thing.
If Perl compiles that code with $var and $myvar as PMCs, and it uses
C<set> to get $var into $myvar, PerlInt won't work right. And if it
uses C<clone> to get $var into $myvar, an object won't work right.
There are two ways to solve this: make a PerlRef class (which will
eventually have to be done anyway) or make a polymorphic assignment
which lets the PMC decide whether it has value or reference semantics.
The included tentative patch implements the latter (namely,
C<valclone>).
So we have (for PMCs):
* C<set>: Always reference semantics
* C<clone>: Always value semantics
* C<valclone>: PMC's choice
Is this a good way to go about solving this problem?
Luke
[ As an added bonus, this patch implements clone_p_p_k and clone_p_p_ki ]
Index: core.ops
===================================================================
RCS file: /cvs/public/parrot/core.ops,v
retrieving revision 1.293
diff -r1.293 core.ops
246,247d245
<
<
612a611,614
> =item B<clone>(out PMC, in PMC, in KEY)
>
> =item B<clone>(out PMC, in PMC, in INTKEY)
>
621c623
< $2->vtable->clone(interpreter, $2, $1);
---
> VTABLE_clone(interpreter, $2, $1);
623a626,664
> }
>
> inline op clone(out PMC, in PMC, in KEY) {
> $1 = pmc_new_noinit(interpreter, $2->vtable->base_type);
> VTABLE_clone_keyed(interpreter, $2, $3, $1);
> goto NEXT();
> }
>
> inline op clone(out PMC, in PMC, in INTKEY) {
> $1 = pmc_new_noinit(interpreter, $2->vtable->base_type);
> VTABLE_clone_keyed_int(interpreter, $2, &$3, $1);
> goto NEXT();
> }
>
> =item B<valclone>(out PMC, in PMC)
>
> =item B<valclone>(out PMC, in PMC, in KEY)
>
> =item B<valclone>(out PMC, in PMC, in INTKEY)
>
> Creates a copy of the pmc in $2 and puts it in $1, respecting reference
> semantics when it should; i.e. PerlInts clone themselves, while PerlArrays
> just do a plain old C<set>.
>
> =cut
>
> inline op valclone(out PMC, in PMC) {
> $1 = VTABLE_valclone(interpreter, $2);
> goto NEXT();
> }
>
> inline op valclone(out PMC, in PMC, in KEY) {
> $1 = VTABLE_valclone_keyed(interpreter, $2, $3);
> goto NEXT();
> }
>
> inline op valclone(out PMC, in PMC, in INTKEY) {
> $1 = VTABLE_valclone_keyed_int(interpreter, $2, &$3);
> goto NEXT();
Index: vtable.tbl
===================================================================
RCS file: /cvs/public/parrot/vtable.tbl,v
retrieving revision 1.38
diff -r1.38 vtable.tbl
30a31,34
> PMC* valclone()
> PMC* valclone_keyed(PMC* key)
> PMC* valclone_keyed_int(INTVAL* key)
>
Index: classes/default.pmc
===================================================================
RCS file: /cvs/public/parrot/classes/default.pmc,v
retrieving revision 1.54
diff -r1.54 default.pmc
172a173,188
> PMC* valclone () {
> return SELF;
> }
>
> PMC* valclone_keyed (PMC* key) {
> internal_exception(ILL_INHERIT,
> "valclone_keyed() not implemented in class '%s'\n",
> caller(INTERP, SELF));
> return NULL;
> }
>
> PMC* valclone_keyed_int (INTVAL* key) {
> PMC* r_key = INT2KEY(INTERP, key);
> return DYNSELF.valclone_keyed(r_key);
> }
>
Index: classes/scalar.pmc
===================================================================
RCS file: /cvs/public/parrot/classes/scalar.pmc,v
retrieving revision 1.6
diff -r1.6 scalar.pmc
22a23,28
> PMC* valclone () {
> PMC* dest = pmc_new(INTERP, SELF->vtable->base_type);
> memcpy(&dest->cache, &SELF->cache, sizeof(UnionVal));
> return dest;
> }
>
Index: t/pmc/perlarray.t
===================================================================
RCS file: /cvs/public/parrot/t/pmc/perlarray.t,v
retrieving revision 1.28
diff -r1.28 perlarray.t
3c3
< use Parrot::Test tests => 22;
---
> use Parrot::Test tests => 23;
1214a1215,1244
> output_is(<<'CODE', <<'OUTPUT', "valclone");
> # Regular clone
> new P0, .PerlArray
> set P0[0], 10
> clone P1, P0
> set P1[0], 20
> set P2, P0[0]
> set P3, P1[0]
> print P2
> print "\n"
> print P3
> print "\n"
>
> # valclone (works like ordinary set in this case)
> new P0, .PerlArray
> set P0[0], 10
> valclone P1, P0
> set P1[0], 20
> set P2, P0[0]
> set P3, P1[0]
> print P2
> print "\n"
> print P3
> print "\n"
> CODE
> 10
> 20
> 20
> 20
> OUTPUT
Index: t/pmc/perlint.t
===================================================================
RCS file: /cvs/public/parrot/t/pmc/perlint.t,v
retrieving revision 1.3
diff -r1.3 perlint.t
3c3
< use Parrot::Test tests => 5;
---
> use Parrot::Test tests => 6;
152a153,183
>
> output_is(<<'CODE', <<'OUTPUT', "valclone");
> # Normal set
> new P0, .PerlInt
> set P0, 10
> set P1, P0
> set P1, 20
> print P0
> print "\n"
> print P1
> print "\n"
>
> # valclone (in .PerlInt's case, excactly like clone)
> new P0, .PerlInt
> set P0, 10
> valclone P1, P0
> set P1, 20
> print P0
> print "\n"
> print P1
> print "\n"
>
> end
> CODE
> 20
> 20
> 10
> 20
> OUTPUT
>
> 1;