summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--op.c56
-rw-r--r--pod/perldiag.pod17
-rw-r--r--t/op/signatures.t93
3 files changed, 164 insertions, 2 deletions
diff --git a/op.c b/op.c
index 666ef26632..6b7bb5b87d 100644
--- a/op.c
+++ b/op.c
@@ -3732,6 +3732,21 @@ Perl_optimize_optree(pTHX_ OP* o)
}
+#define warn_implicit_snail_cvsig(o) S_warn_implicit_snail_cvsig(aTHX_ o)
+static void
+S_warn_implicit_snail_cvsig(pTHX_ OP *o)
+{
+ CV *cv = PL_compcv;
+ while(cv && CvEVAL(cv))
+ cv = CvOUTSIDE(cv);
+
+ if(cv && CvSIGNATURE(cv))
+ Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__ARGS_ARRAY_WITH_SIGNATURES),
+ "Implicit use of @_ in %s with signatured subroutine is experimental", OP_DESC(o));
+}
+
+#define OP_ZOOM(o) (OP_TYPE_IS(o, OP_NULL) ? cUNOPx(o)->op_first : (o))
+
/* helper for optimize_optree() which optimises one op then recurses
* to optimise any children.
*/
@@ -3775,6 +3790,47 @@ S_optimize_op(pTHX_ OP* o)
}
break;
+ case OP_RV2AV:
+ {
+ OP *first = (o->op_flags & OPf_KIDS) ? cUNOPo->op_first : NULL;
+ CV *cv = PL_compcv;
+ while(cv && CvEVAL(cv))
+ cv = CvOUTSIDE(cv);
+
+ if(cv && CvSIGNATURE(cv) &&
+ OP_TYPE_IS(first, OP_GV) && cGVOPx_gv(first) == PL_defgv) {
+ OP *parent = op_parent(o);
+ while(OP_TYPE_IS(parent, OP_NULL))
+ parent = op_parent(parent);
+
+ Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__ARGS_ARRAY_WITH_SIGNATURES),
+ "Use of @_ in %s with signatured subroutine is experimental", OP_DESC(parent));
+ }
+ break;
+ }
+
+ case OP_SHIFT:
+ case OP_POP:
+ if(!CvUNIQUE(PL_compcv) && !(o->op_flags & OPf_KIDS))
+ warn_implicit_snail_cvsig(o);
+ break;
+
+ case OP_ENTERSUB:
+ if(!(o->op_flags & OPf_STACKED))
+ warn_implicit_snail_cvsig(o);
+ break;
+
+ case OP_GOTO:
+ {
+ OP *first = (o->op_flags & OPf_KIDS) ? cUNOPo->op_first : NULL;
+ OP *ffirst;
+ if(OP_TYPE_IS(first, OP_SREFGEN) &&
+ (ffirst = OP_ZOOM(cUNOPx(first)->op_first)) &&
+ OP_TYPE_IS(ffirst, OP_RV2CV))
+ warn_implicit_snail_cvsig(o);
+ break;
+ }
+
default:
break;
}
diff --git a/pod/perldiag.pod b/pod/perldiag.pod
index 07b1166703..62dca08f27 100644
--- a/pod/perldiag.pod
+++ b/pod/perldiag.pod
@@ -2824,6 +2824,15 @@ would otherwise result in the same message being repeated.
Failure of user callbacks dispatched using the C<G_KEEPERR> flag could
also result in this warning. See L<perlcall/G_KEEPERR>.
+=item Implicit use of @_ in %s with signatured subroutine is experimental
+
+(S experimental::args_array_with_signatures) An expression that implicitly
+involves the C<@_> arguments array was found in a subroutine that uses a
+signature. This is experimental because the interaction between the
+arguments array and parameter handling via signatures is not guaranteed
+to remain stable in any future version of Perl, and such code should be
+avoided.
+
=item Incomplete expression within '(?[ ])' in regex; marked by S<<-- HERE>
in m/%s/
@@ -7252,6 +7261,14 @@ you can write it as C<push(@tied_array,())> to avoid this warning.
(F) The "use" keyword is recognized and executed at compile time, and
returns no useful value. See L<perlmod>.
+=item Use of @_ in %s with signatured subroutine is experimental
+
+(S experimental::args_array_with_signatures) An expression involving the
+C<@_> arguments array was found in a subroutine that uses a signature.
+This is experimental because the interaction between the arguments
+array and parameter handling via signatures is not guaranteed to remain
+stable in any future version of Perl, and such code should be avoided.
+
=item Use of bare << to mean <<"" is forbidden
(F) You are now required to use the explicitly quoted form if you wish
diff --git a/t/op/signatures.t b/t/op/signatures.t
index d22d11b517..e70215afca 100644
--- a/t/op/signatures.t
+++ b/t/op/signatures.t
@@ -434,7 +434,10 @@ like $@, _create_flexible_mismatch_regexp('main::t128', 3, 2);
is $a, 123;
sub t130 { join(",", @_).";".scalar(@_) }
-sub t131 ($a = 222, $b = goto &t130) { "$a/$b" }
+{
+ no warnings 'experimental::args_array_with_signatures';
+ sub t131 ($a = 222, $b = goto &t130) { "$a/$b" }
+}
is prototype(\&t131), undef;
is eval("t131()"), ";0";
is eval("t131(0)"), "0;1";
@@ -1380,13 +1383,15 @@ is scalar(t145()), undef;
}
is ref(t149()), "ARRAY", "t149: closure can make new lexical a ref";
+ # Quiet the 'use of @_ is experimental' warnings
+ no warnings 'experimental::args_array_with_signatures';
+
sub t150 ($a = do {@_ = qw(a b c); 1}, $b = 2) {
is $a, 1, "t150: a: growing \@_";
is $b, "b", "t150: b: growing \@_";
}
t150();
-
sub t151 ($a = do {tie @_, 'Tie::StdArray'; @_ = qw(a b c); 1}, $b = 2) {
is $a, 1, "t151: a: tied \@_";
is $b, "b", "t151: b: tied \@_";
@@ -1608,6 +1613,90 @@ while(<$kh>) {
'f($1)';
}
+# check that various uses of @_ inside signatured subs causes "experimental"
+# warnings at compiletime
+{
+ sub warnings_from {
+ my ($code, $run) = @_;
+ my $warnings = "";
+ local $SIG{__WARN__} = sub { $warnings .= join "", @_ };
+ my $cv = eval qq{ sub(\$x) { $code }} or die "Cannot eval() - $@";
+ $run and $cv->(123);
+ return $warnings;
+ }
+
+ sub snailwarns_ok {
+ my ($opname, $code) = @_;
+ my $warnings = warnings_from $code;
+ ok($warnings =~ m/[Uu]se of \@_ in $opname with signatured subroutine is experimental at \(eval /,
+ "`$code` warns of experimental \@_") or
+ diag("Warnings were:\n$warnings");
+ }
+
+ sub snailwarns_runtime_ok {
+ my ($opname, $code) = @_;
+ my $warnings = warnings_from $code, 1;
+ ok($warnings =~ m/[Uu]se of \@_ in $opname with signatured subroutine is experimental at \(eval /,
+ "`$code` warns of experimental \@_") or
+ diag("Warnings were:\n$warnings");
+ }
+
+ sub not_snailwarns_ok {
+ my ($code) = @_;
+ my $warnings = warnings_from $code;
+ ok($warnings !~ m/[Uu]se of \@_ in .* with signatured subroutine is experimental at \(eval /,
+ "`$code` warns of experimental \@_") or
+ diag("Warnings were:\n$warnings");
+ }
+
+ # implicit @_
+ snailwarns_ok 'shift', 'shift';
+ snailwarns_ok 'pop', 'pop';
+ snailwarns_ok 'goto', 'goto &SUB'; # tail-call
+ snailwarns_ok 'subroutine entry', '&SUB'; # perl4-style
+
+ # explicit @_
+ snailwarns_ok 'shift', 'shift @_';
+ snailwarns_ok 'pop', 'pop @_';
+ snailwarns_ok 'array element', '$_[0]';
+ snailwarns_ok 'array element', 'my $one = 1; $_[$one]';
+ snailwarns_ok 'push', 'push @_, 1';
+ snailwarns_ok 'unshift', 'unshift @_, 9';
+ snailwarns_ok 'splice', 'splice @_, 1, 2, 3';
+ snailwarns_ok 'keys on array', 'keys @_';
+ snailwarns_ok 'values on array', 'values @_';
+ snailwarns_ok 'each on array', 'each @_';
+ snailwarns_ok 'print', 'print "a", @_, "z"';
+ snailwarns_ok 'subroutine entry', 'func("a", @_, "z")';
+
+ # Also warns about @_ inside the signature params
+ like(warnings_from('sub ($x = shift) { }'),
+ qr/^Implicit use of \@_ in shift with signatured subroutine is experimental at \(eval /,
+ 'Warns of experimental @_ in param default');
+ like(warnings_from('sub ($x = $_[0]) { }'),
+ qr/^Use of \@_ in array element with signatured subroutine is experimental at \(eval /,
+ 'Warns of experimental @_ in param default');
+
+ # Inside eval() still counts, at runtime
+ snailwarns_runtime_ok 'array element', 'eval q( $_[0] )';
+
+ # still permitted without warning
+ not_snailwarns_ok 'my $f = sub { my $y = shift; }';
+ not_snailwarns_ok 'my $f = sub { my $y = $_[0]; }';
+ not_snailwarns_ok '\&SUB';
+}
+
+# Warnings can be disabled
+{
+ my $warnings = "";
+ local $SIG{__WARN__} = sub { $warnings .= join "", @_ };
+ eval q{
+ no warnings 'experimental::snail_in_signatures';
+ sub($x) { @_ = (1,2,3) }
+ };
+ is($warnings, "", 'No warnings emitted within scope of no warnings "experimental"');
+}
+
done_testing;
1;