Skip to content

Commit 600a61c

Browse files
committed
Process $self in method subs as a subsignature parameter
This has at least three advantages: * Removes more special-case code from `class.c` and makes generated optrees more similar to others * Ensures that no other ops appear in the optree before signature handling (as the OP_METHSTART used to). This will be useful for upcoming faster-signatures changes * Corrects the previous "off-by-one" error in parameter counts as reported by the argument count check exception messages
1 parent 28cb54b commit 600a61c

File tree

6 files changed

+78
-13
lines changed

6 files changed

+78
-13
lines changed

class.c

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,25 @@ Perl_class_prepare_method_parse(pTHX_ CV *cv)
953953
CvIsMETHOD_on(cv);
954954
}
955955

956+
#define find_op_methstart(o) S_find_op_methstart(aTHX_ o)
957+
static OP *
958+
S_find_op_methstart(pTHX_ OP *o)
959+
{
960+
if(o->op_type == OP_METHSTART)
961+
return o;
962+
963+
if(!(o->op_flags & OPf_KIDS))
964+
return NULL;
965+
966+
for(OP *kid = cUNOPo->op_first; kid; kid = OpSIBLING(kid)) {
967+
OP *methstart = find_op_methstart(kid);
968+
if(methstart)
969+
return methstart;
970+
}
971+
972+
return NULL;
973+
}
974+
956975
OP *
957976
Perl_class_wrap_method_body(pTHX_ OP *o)
958977
{
@@ -1010,7 +1029,18 @@ Perl_class_wrap_method_body(pTHX_ OP *o)
10101029
if(o->op_type != OP_LINESEQ)
10111030
o = newLISTOP(OP_LINESEQ, 0, o, NULL);
10121031

1013-
op_sibling_splice(o, NULL, 0, newUNOP_AUX(OP_METHSTART, 0, NULL, aux));
1032+
if(CvSIGNATURE(PL_compcv)) {
1033+
/* A signatured method has already injected the OP_METHSTART; we just
1034+
* have to find it and attach the aux structure to it
1035+
*/
1036+
OP *methstartop = find_op_methstart(o);
1037+
assert(methstartop);
1038+
assert(!cUNOP_AUXx(methstartop)->op_aux);
1039+
1040+
cUNOP_AUXx(methstartop)->op_aux = aux;
1041+
}
1042+
else
1043+
op_sibling_splice(o, NULL, 0, newUNOP_AUX(OP_METHSTART, 0, NULL, aux));
10141044

10151045
return o;
10161046
}
@@ -1108,13 +1138,14 @@ apply_field_attribute_reader(pTHX_ PADNAME *pn, SV *value)
11081138

11091139
I32 save_ix = block_start(TRUE);
11101140

1111-
subsignature_start();
1112-
11131141
PADOFFSET padix;
11141142

11151143
padix = pad_add_name_pvs("$self", 0, NULL, NULL);
11161144
assert(padix == PADIX_SELF);
11171145

1146+
subsignature_start();
1147+
CvSIGNATURE_on(PL_compcv);
1148+
11181149
OP *sigop = subsignature_finish();
11191150

11201151
padix = pad_import_field(pn);
@@ -1175,13 +1206,14 @@ apply_field_attribute_writer(pTHX_ PADNAME *pn, SV *value)
11751206

11761207
I32 save_ix = block_start(TRUE);
11771208

1178-
subsignature_start();
1179-
11801209
PADOFFSET padix;
11811210

11821211
padix = pad_add_name_pvs("$self", 0, NULL, NULL);
11831212
assert(padix == PADIX_SELF);
11841213

1214+
subsignature_start();
1215+
CvSIGNATURE_on(PL_compcv);
1216+
11851217
/* param pad variable doesn't technically need a name, so don't bother as
11861218
* reusing the field name will provoke a warning */
11871219
PADOFFSET param_padix = padix = pad_add_name_pvn("$", 1, 0, NULL, NULL);

lib/B/Deparse.pm

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,6 +1238,8 @@ sub deparse_argops {
12381238
# skip trailing nextstate
12391239
last if $$o == $$last;
12401240

1241+
next if $cv->CvFLAGS & CVf_IsMETHOD and $o->name eq "methstart";
1242+
12411243
# OP_NEXTSTATE
12421244
return unless $o->name =~ /^(next|db)state$/;
12431245
return if $o->label;
@@ -1296,6 +1298,13 @@ sub deparse_argops {
12961298

12971299
}
12981300

1301+
if ($cv->CvFLAGS & CVf_IsMETHOD) {
1302+
# Remove the implied `$self` argument
1303+
warn "Expected first signature argument to be named \$self"
1304+
unless @sig and $sig[0] eq '$self';
1305+
shift @sig;
1306+
}
1307+
12991308
while (++$last_ix < $params) {
13001309
push @sig, $last_ix < $mandatory ? '$' : '$=';
13011310
}
@@ -1361,10 +1370,6 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
13611370
my $is_list = ($lineseq->name eq "lineseq");
13621371
my $firstop = $is_list ? $lineseq->first : $lineseq;
13631372

1364-
if ($is_method and $firstop->name eq "methstart") {
1365-
$firstop = $firstop->sibling;
1366-
}
1367-
13681373
# Try to deparse first subtree as a signature if possible.
13691374
# Top of signature subtree has an ex-argcheck as a placeholder
13701375
if ( $has_sig

op.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16516,6 +16516,18 @@ Perl_subsignature_start(pTHX)
1651616516

1651716517
SAVEVPTR(PL_parser->signature);
1651816518
PL_parser->signature = signature;
16519+
16520+
/* TODO: This should ideally be performed by some sort of "magic" or
16521+
* "hook" mechanism on PL_compcv that class.c installed, thus decoupling
16522+
* this otherwise tightly-coupled mechanism here
16523+
*/
16524+
if(CvIsMETHOD(PL_compcv)) {
16525+
assert(PadnamelistMAX(PL_comppad_name) >= 1);
16526+
/* PADIX_SELF == 1 */
16527+
assert(PadnamePV(PadnamelistARRAY(PL_comppad_name)[1])[0] == '$');
16528+
subsignature_append_positional(1, 0, NULL);
16529+
subsignature_append_fence_op(newUNOP_AUX(OP_METHSTART, OPpSELF_IN_PAD << 8, NULL, NULL));
16530+
}
1651916531
}
1652016532

1652116533
/* Appends another arbitrary optree into the accumulated set of signature-

pod/perldelta.pod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ here, but most should go in the L</Performance Enhancements> section.
3737

3838
[ List each enhancement as a =head2 entry ]
3939

40+
=head2 Reported argument counts in C<method> signatures now account for C<$self>
41+
42+
In previous versions of Perl, the exception message thrown by a C<method>
43+
subroutine with a signature when it does not receive an appropriate number of
44+
arguments to match its declared parameters failed to account for the implied
45+
C<$self> parameter, causing the numbers in the message to be 1 fewer than
46+
intended.
47+
48+
This has now been fixed, so messages report the correct number of arguments
49+
including the object invocant.
50+
4051
=head1 Security
4152

4253
XXX Any security-related notices go here. In particular, any security

t/class/accessor.t

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ no warnings 'experimental::class';
3333
# Read accessor does not permit arguments
3434
ok(!eval { $o->s("value") },
3535
'Reader accessor fails with argument');
36-
like($@, qr/^Too many arguments for subroutine \'Testcase1::s\' \(got 1; expected 0\) at /,
36+
like($@, qr/^Too many arguments for subroutine \'Testcase1::s\' \(got 2; expected 1\) at /,
3737
'Failure from argument to accessor');
3838
}
3939

@@ -51,11 +51,11 @@ no warnings 'experimental::class';
5151
# Write accessor wants exactly one argument
5252
ok(!eval { $o->set_s() },
5353
'Reader accessor fails with no argument');
54-
like($@, qr/^Too few arguments for subroutine \'Testcase2::set_s\' \(got 0; expected 1\) at /,
54+
like($@, qr/^Too few arguments for subroutine \'Testcase2::set_s\' \(got 1; expected 2\) at /,
5555
'Failure from argument to accessor');
5656
ok(!eval { $o->set_s(1, 2) },
5757
'Reader accessor fails with 2 arguments');
58-
like($@, qr/^Too many arguments for subroutine \'Testcase2::set_s\' \(got 2; expected 1\) at /,
58+
like($@, qr/^Too many arguments for subroutine \'Testcase2::set_s\' \(got 3; expected 2\) at /,
5959
'Failure from argument to accessor');
6060
}
6161

t/class/method.t

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ no warnings 'experimental::class';
2121
is($obj->retself, $obj, '$self inside method');
2222
}
2323

24-
# methods have signatures; signatures do not capture $self
24+
# methods have signatures
2525
{
2626
# Turn off the 'signatures' feature to prove that 'method' is always
2727
# signatured even without it
@@ -34,6 +34,11 @@ no warnings 'experimental::class';
3434
my $obj = Testcase2->new;
3535
is($obj->retfirst, 123, 'method signature params work');
3636
is($obj->retfirst(456), 456, 'method signature params skip $self');
37+
38+
# argument counts take account of implicit $self
39+
my $e = eval { $obj->retfirst(1, 2) } ? undef : $@;
40+
like($e, qr/^Too many arguments for subroutine 'Testcase2::retfirst' \(got 3; expected at most 2\) /,
41+
'method signature fails with too many arguments');
3742
}
3843

3944
# methods can still capture regular package lexicals

0 commit comments

Comments
 (0)