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