683572
From b061e315b4eac4d82edb3ca14581805417a68936 Mon Sep 17 00:00:00 2001
683572
From: Tony Cook <tony@develop-help.com>
683572
Date: Wed, 11 Sep 2019 11:50:23 +1000
683572
Subject: [PATCH] (perl #125557) correctly handle overload for bin/oct floats
683572
MIME-Version: 1.0
683572
Content-Type: text/plain; charset=UTF-8
683572
Content-Transfer-Encoding: 8bit
683572
683572
The hexfp code doesn't check that the shift is 4, and so also
683572
accepts binary and octal fp numbers.
683572
683572
Unfortunately the call to S_new_constant() always passed a prefix
683572
of 0x, so overloading would be trying to parse the wrong number.
683572
683572
Another option is to simply allow only hex floats, though some work
683572
was done in 131894 to improve oct/bin float support.
683572
683572
Petr Písař: Ported to 5.30.1 from
683572
2cb5a7e8af11acb0eca22421ec5a4df7ef18e2a9.
683572
683572
Signed-off-by: Petr Písař <ppisar@redhat.com>
683572
---
683572
 t/op/hexfp.t | 16 +++++++++++++++-
683572
 toke.c       | 21 ++++++++++++++++-----
683572
 2 files changed, 31 insertions(+), 6 deletions(-)
683572
683572
diff --git a/t/op/hexfp.t b/t/op/hexfp.t
683572
index 64f8136..0f239d4 100644
683572
--- a/t/op/hexfp.t
683572
+++ b/t/op/hexfp.t
683572
@@ -10,7 +10,7 @@ use strict;
683572
 
683572
 use Config;
683572
 
683572
-plan(tests => 123);
683572
+plan(tests => 125);
683572
 
683572
 # Test hexfloat literals.
683572
 
683572
@@ -277,6 +277,20 @@ is(0b1p0, 1);
683572
 is(0b10p0, 2);
683572
 is(0b1.1p0, 1.5);
683572
 
683572
+# previously these would pass "0x..." to the overload instead of the appropriate
683572
+# "0b" or "0" prefix.
683572
+fresh_perl_is(<<'CODE', "1", {}, "overload binary fp");
683572
+use overload;
683572
+BEGIN { overload::constant float => sub { return eval $_[0]; }; }
683572
+print 0b0.1p1;
683572
+CODE
683572
+
683572
+fresh_perl_is(<<'CODE', "1", {}, "overload octal fp");
683572
+use overload;
683572
+BEGIN { overload::constant float => sub { return eval $_[0]; }; }
683572
+print 00.1p3;
683572
+CODE
683572
+
683572
 # sprintf %a/%A testing is done in sprintf2.t,
683572
 # trickier than necessary because of long doubles,
683572
 # and because looseness of the spec.
683572
diff --git a/toke.c b/toke.c
683572
index 03c4f2b..3fa20dc 100644
683572
--- a/toke.c
683572
+++ b/toke.c
683572
@@ -10966,6 +10966,7 @@ Perl_scan_num(pTHX_ const char *start, YYSTYPE* lvalp)
683572
     const char *lastub = NULL;		/* position of last underbar */
683572
     static const char* const number_too_long = "Number too long";
683572
     bool warned_about_underscore = 0;
683572
+    I32 shift; /* shift per digit for hex/oct/bin, hoisted here for fp */
683572
 #define WARN_ABOUT_UNDERSCORE() \
683572
 	do { \
683572
 	    if (!warned_about_underscore) { \
683572
@@ -11012,8 +11013,6 @@ Perl_scan_num(pTHX_ const char *start, YYSTYPE* lvalp)
683572
 	{
683572
 	  /* variables:
683572
 	     u		holds the "number so far"
683572
-	     shift	the power of 2 of the base
683572
-			(hex == 4, octal == 3, binary == 1)
683572
 	     overflowed	was the number more than we can hold?
683572
 
683572
 	     Shift is used when we add a digit.  It also serves as an "are
683572
@@ -11022,7 +11021,6 @@ Perl_scan_num(pTHX_ const char *start, YYSTYPE* lvalp)
683572
 	   */
683572
 	    NV n = 0.0;
683572
 	    UV u = 0;
683572
-	    I32 shift;
683572
 	    bool overflowed = FALSE;
683572
 	    bool just_zero  = TRUE;	/* just plain 0 or binary number? */
683572
 	    static const NV nvshift[5] = { 1.0, 2.0, 4.0, 8.0, 16.0 };
683572
@@ -11369,8 +11367,21 @@ Perl_scan_num(pTHX_ const char *start, YYSTYPE* lvalp)
683572
         if (hexfp) {
683572
             floatit = TRUE;
683572
             *d++ = '0';
683572
-            *d++ = 'x';
683572
-            s = start + 2;
683572
+            switch (shift) {
683572
+            case 4:
683572
+                *d++ = 'x';
683572
+                s = start + 2;
683572
+                break;
683572
+            case 3:
683572
+                s = start + 1;
683572
+                break;
683572
+            case 1:
683572
+                *d++ = 'b';
683572
+                s = start + 2;
683572
+                break;
683572
+            default:
683572
+                NOT_REACHED; /* NOTREACHED */
683572
+            }
683572
         }
683572
 
683572
 	/* read next group of digits and _ and copy into d */
683572
-- 
683572
2.21.0
683572