This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make constant folding use the right hints
authorFather Chrysostomos <sprout@cpan.org>
Fri, 9 Aug 2013 21:24:23 +0000 (14:24 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 10 Aug 2013 02:44:13 +0000 (19:44 -0700)
This was brought up in ticket #117855.

PL_compiling is a cop-sized static buffer (inside the interpreter
struct under threads) that stores information during compile time
(such as file name and line number) that gets copied to each cop (con-
trol op; aka state op or nextstate op) as it is created.

Some values are not actually stored in PL_compiling, such as the
current stash (PL_curstash is used instead) and the current hints
(PL_hints is used).

The ops in each statement are created before that statement’s cop
is created.

At run time, each cop is executed at the start of the statement and
sets PL_curcop to point to itself, so that operators within that
statement can get information from PL_curcop.

Constant folding was copying the contents of PL_compiling into a tem-
porary cop used to execute the ops being folded.  That constant fold-
ing happened of course before the cop was created for that statement.

Now it just happened that commit a0ed51b321 back in 1998 modified
newSTATEOP to copy hints to PL_compiling in addition to the new cop
being created.  Presumably that was to fix the type of bug that this
commit is addressing.  (Presumably this commit renders those changes
unnecessary.)  That meant that most of the time constant folding would
see the right hints.

That fails, though, when it comes to the first statement in a string
eval.  When you do eval("uc(ä)"), the constant folding happens when
PL_compiling.cop_hints still points to whatever value it had before
the eval was executed; i.e., an unpredictable value.  Slight changes
to unrelated scopes (and, apparently, using a different compiler*) can
cause the result of that string eval to change.

The solution is to set the hints from PL_hints explicitly when doing
constant folding.

* <https://rt.perl.org/rt3/Ticket/Display.html?id=117855#txn-1241613>ff.
  I never got to the bottom of why the compiler would make a diffe-
  rence here.  Finding out would involve setting a watchpoint on
  PL_compiling.cop_hints in a C debugger and then stepping through
  the thousands of times PL_compiling changes, which is too much
  work.  Nevertheless, I know this fix is correct, as it changes
  PL_compiling.cop_hints from having a non-deterministic value during
  constant folding to having a predictable one.

op.c
t/op/lc.t

diff --git a/op.c b/op.c
index 09ab4f3..fc0f130 100644 (file)
--- a/op.c
+++ b/op.c
@@ -3275,6 +3275,7 @@ S_fold_constants(pTHX_ OP *o)
     /* Verify that we don't need to save it:  */
     assert(PL_curcop == &PL_compiling);
     StructCopy(&PL_compiling, &not_compiling, COP);
+    CopHINTS_set(&not_compiling, PL_hints);
     PL_curcop = &not_compiling;
     /* The above ensures that we run with all the correct hints of the
        currently compiling COP, but that IN_PERL_RUNTIME is not true. */
index 5a71163..ae15625 100644 (file)
--- a/t/op/lc.t
+++ b/t/op/lc.t
@@ -10,7 +10,7 @@ BEGIN {
 
 use feature qw( fc );
 
-plan tests => 128;
+plan tests => 129;
 
 is(lc(undef),     "", "lc(undef) is ''");
 is(lcfirst(undef), "", "lcfirst(undef) is ''");
@@ -280,3 +280,16 @@ is(lc("\x{1E9E}"), "\x{df}", "lc(LATIN CAPITAL LETTER SHARP S)");
     is(uc("\xe0"), "\xe0", "uc of above-ASCII Latin1 is itself under use bytes");
     is(ucfirst("\xe0"), "\xe0", "ucfirst of above-ASCII Latin1 is itself under use bytes");
 }
+
+# Brought up in ticket #117855: Constant folding applied to uc() should use
+# the right set of hints.
+fresh_perl_like(<<'constantfolding', qr/^(\d+),\1\z/, {},
+    my $function = "uc";
+    my $char = "\xff";
+    {
+        use feature 'unicode_strings';
+        print ord uc($char), ",",
+              ord eval "$function('$char')", "\n";
+    }
+constantfolding
+    'folded uc() in string eval uses the right hints');