summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2018-02-10 15:27:59 +0000
committerDavid Mitchell <davem@iabyn.com>2018-02-19 22:06:49 +0000
commitaf39014264c90cfc5a35e4f6e39ba038a8fb0c29 (patch)
treef74e9111855c24fd9940e12868280470976270f8 /lib
parent16fe3f8a9e2ea46c1f8b8078916520fd6bf0a0b1 (diff)
downloadperl-af39014264c90cfc5a35e4f6e39ba038a8fb0c29.tar.gz
redo magic/overload handing in pp_multiconcat
The way pp_multiconcat handles things like tieing and overloading doesn't work very well at the moment. There's a lot of code to handle edge cases, and there are still open bugs. The basic algorithm in pp_multiconcat is to first stringify (i.e. call SvPV() on) *all* args, then use the obtained values to calculate the total length and utf8ness required, then do a single SvGROW and copy all the bytes from all the args. This ordering is wrong when variables with visible side effects, such as tie/overload, are encountered. The current approach is to stringify args up until such an arg is encountered, concat all args up until that one together via the normal fast route, then jump to a special block of code which concats any remaining args one by one the "hard" way, handling overload etc. This is problematic because we sometimes need to go back in time. For example in ($undef . $overloaded), we're supposed to call $overloaded->concat($undef, reverse=1) so to speak, but by the time of the method call, we've already tried to stringify $undef and emitted a spurious 'uninit var' warning. The new approach taken in this commit is to: 1) Bail out of the stringify loop under a greater range of problematical variable classes - namely we stop when encountering *anything* which might cause external effects, so in addition to tied and overloaded vars, we now stop for any sort of get magic, or any undefined value where warnings are in scope. 2) If we bail out, we throw away any stringification results so far, and concatenate *all* args the slow way, even ones we're already stringified. This solves the "going back in time" problem mentioned above. It's safe because the only vars that get processed twice are ones for which the first stringification could have no side effects. The slow concat loop now uses S_do_concat(), which is a new static inline function which implements the main body of pp_concat() - so they share identical code. An intentional side-effect of this commit is to fix three tickets: RT #132783 RT #132827 RT #132595 so tests for them are included in this commit. One effect of this commit is that string concatenation of magic or undefined vars will now be slower than before, e.g. "pid=$$" "value=$undef" but they will probably still be faster than before pp_multiconcat was introduced.
Diffstat (limited to 'lib')
-rw-r--r--lib/overload.t25
1 files changed, 24 insertions, 1 deletions
diff --git a/lib/overload.t b/lib/overload.t
index 2afa6cf437..a053810104 100644
--- a/lib/overload.t
+++ b/lib/overload.t
@@ -48,7 +48,7 @@ package main;
$| = 1;
BEGIN { require './test.pl'; require './charset_tools.pl' }
-plan tests => 5338;
+plan tests => 5340;
use Scalar::Util qw(tainted);
@@ -3047,3 +3047,26 @@ package RT132385 {
# ditto with a mutator
::is($o .= $r1, "obj-ref1", "RT #132385 o.=r1");
}
+
+# the RHS of an overloaded .= should be passed as-is to the overload
+# method, rather than being stringified or otherwise being processed in
+# such a way that it triggers an undef warning
+package RT132783 {
+ use warnings;
+ use overload '.=' => sub { return "foo" };
+ my $w = 0;
+ local $SIG{__WARN__} = sub { $w++ };
+ my $undef;
+ my $ov = bless [];
+ $ov .= $undef;
+ ::is($w, 0, "RT #132783 - should be no warnings");
+}
+
+# changing the overloaded object to a plain string within an overload
+# method should be permanent.
+package RT132827 {
+ use overload '""' => sub { $_[0] = "a" };
+ my $ov = bless [];
+ my $b = $ov . "b";
+ ::is(ref \$ov, "SCALAR", "RT #132827");
+}