Commit 97df6ed824 for perl

commit 97df6ed824bf359753a008572dbe3112368ed371
Author: Richard Leach <rich+perl@hyphen-dash-hyphen.info>
Date:   Sat Nov 7 22:00:52 2026 +0000

    PL_sv_zero - remove SVf_POK to make it seem number-first

    `PL_sv_zero`, introduced in 5.28, has a zero numeric value and a "0"
    string value. Its flags did not indicate which value should be treated
    as authoritative when serializing or encoding to an external format.
    This could lead to what was originally a numerical return value being
    output as the string representation instead, which might not have the
    same meaning (e.g. in JSON).

    The flags on `PL_sv_zero` have been modified so that it appears to
    have been created as a number and later gained a string representation.
    This is intended as an aid to serializers that differentiate between
    "created as number" and "created as string".

diff --git a/lib/builtin.t b/lib/builtin.t
index e01ddb45ca..0d245d57c3 100644
--- a/lib/builtin.t
+++ b/lib/builtin.t
@@ -752,6 +752,17 @@ EOS
     is($h{123}, 456, "Numerical OP_CONST SVs in hash init are created_as_number() when hekified");
 }

+# array element counts are numbers, even if that number is zero
+{
+    use builtin qw( created_as_number created_as_string);
+    my @arr;
+    ok(created_as_number(scalar @arr), 'scalar @arr on empty array created_as_number');
+    ok(!created_as_string(scalar @arr), 'scalar @arr on empty array not created_as_string');
+    push @arr, "abcde";
+    ok(created_as_number(scalar @arr), 'scalar @arr on single element array created_as_number');
+    ok(!created_as_string(scalar @arr), 'scalar @arr on single element array not created_as_string');
+}
+
 # vim: tabstop=4 shiftwidth=4 expandtab autoindent softtabstop=4

 done_testing();
diff --git a/pod/perldelta.pod b/pod/perldelta.pod
index 5bca15ac88..4002c085cf 100644
--- a/pod/perldelta.pod
+++ b/pod/perldelta.pod
@@ -528,6 +528,21 @@ refaliasing and the C<builtin::indexed> function. [GH #24999]
         say "Array number [$idx] contains <@arr>";
     }

+=item *
+
+C<PL_sv_zero>, introduced in 5.28, has a zero numeric value and a "0"
+string value. Its flags did not indicate which value should be treated
+as authoritative when serializing or encoding to an external format.
+This could lead to what was originally a numerical return value being
+output as the string representation instead, which might not have the
+same meaning (e.g. in JSON).
+
+The flags on C<PL_sv_zero> have been modified so that it appears to have
+been created as a number and then later gained a string representation.
+This change is intended to make it easier for serialization modules to
+output C<0> rather than C<"0">, provided that such modules actually do
+differentiate between "created as number" and "created as string".
+
 =back

 =head1 Known Problems
diff --git a/sv.c b/sv.c
index 8a508a3f7d..3630de0ee6 100644
--- a/sv.c
+++ b/sv.c
@@ -18170,7 +18170,7 @@ Perl_init_constants(pTHX)
     SvREFCNT(&PL_sv_zero)	= SvREFCNT_IMMORTAL;
     SvFLAGS(&PL_sv_zero)	= SVt_PVNV|SVf_READONLY|SVf_PROTECT
                                   |SVp_IOK|SVf_IOK|SVp_NOK|SVf_NOK
-                                  |SVp_POK|SVf_POK
+                                  |SVp_POK
                                   |SVs_PADTMP;

     SvPV_set(&PL_sv_no, (char*)PL_No);
diff --git a/t/op/svflags.t b/t/op/svflags.t
index 0c7f27b46d..703ea6d26b 100644
--- a/t/op/svflags.t
+++ b/t/op/svflags.t
@@ -10,7 +10,7 @@ BEGIN {
 # Tests the new documented mechanism for determining the original type
 # of an SV.

-plan tests => 23;
+plan tests => 24;
 use strict;
 use B qw(svref_2object SVf_IOK SVf_NOK SVf_POK);

@@ -122,3 +122,9 @@ s'foo'bar'
 CODE
 unlike($got, qr/Conditional jump or move depends on uninitialised value/,
         'All fields initialized in SVt_PV[IV|NV|MG] copies');
+
+my @arr;
+# pp_padav pushes $PL_sv_zero to the stack here
+my $sc = scalar @arr;
+$xobj = svref_2object(\$sc);
+is($xobj->FLAGS & (SVf_IOK | SVf_POK), SVf_IOK, "PL_sv_zero behaves as number (not string) first");