From e5a00b6bcd2f6e2aff93e800a42c6a7f4cd948c3 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 19 Jan 2024 15:00:35 +0000 Subject: [PATCH] Have `use VERSION` and `perl -E` import a corresponding `use builtin` bundle --- builtin.c | 13 +++++++------ embed.fnc | 7 +++++++ embed.h | 5 +++++ op.c | 10 ++++++++++ pod/perlfunc.pod | 18 ++++++++++++------ pod/perlrun.pod | 4 ++-- proto.h | 17 +++++++++++++++++ t/comp/use.t | 14 +++++++++++++- t/run/switches.t | 7 ++++++- toke.c | 3 ++- 10 files changed, 81 insertions(+), 17 deletions(-) diff --git a/builtin.c b/builtin.c index 8996eb58c5b8..415c84d00d27 100644 --- a/builtin.c +++ b/builtin.c @@ -12,6 +12,7 @@ */ #include "EXTERN.h" +#define PERL_IN_BUILTIN_C #include "perl.h" #include "XSUB.h" @@ -39,8 +40,8 @@ static void S_warn_experimental_builtin(pTHX_ const char *name) /* These three utilities might want to live elsewhere to be reused from other * code sometime */ -#define prepare_export_lexical() S_prepare_export_lexical(aTHX) -static void S_prepare_export_lexical(pTHX) +void +Perl_prepare_export_lexical(pTHX) { assert(PL_compcv); @@ -59,8 +60,8 @@ static void S_export_lexical(pTHX_ SV *name, SV *sv) PL_curpad[off] = SvREFCNT_inc(sv); } -#define finish_export_lexical() S_finish_export_lexical(aTHX) -static void S_finish_export_lexical(pTHX) +void +Perl_finish_export_lexical(pTHX) { intro_my(); @@ -603,8 +604,8 @@ static bool S_cv_is_builtin(pTHX_ CV *cv) return file && strEQ(file, __FILE__); } -#define import_builtin_bundle(ver, do_unimport) S_import_builtin_bundle(aTHX_ ver, do_unimport) -static void S_import_builtin_bundle(pTHX_ U16 ver, bool do_unimport) +void +Perl_import_builtin_bundle(pTHX_ U16 ver, bool do_unimport) { SV *ampname = sv_newmortal(); diff --git a/embed.fnc b/embed.fnc index 61024df5975c..363fad81f09d 100644 --- a/embed.fnc +++ b/embed.fnc @@ -4076,6 +4076,13 @@ i |bool |PerlEnv_putenv |NN char *str #if defined(PERL_IN_AV_C) S |MAGIC *|get_aux_mg |NN AV *av #endif +#if defined(PERL_IN_BUILTIN_C) || defined(PERL_IN_OP_C) +p |void |finish_export_lexical +p |void |import_builtin_bundle \ + |U16 ver \ + |bool do_unimport +p |void |prepare_export_lexical +#endif #if defined(PERL_IN_CLASS_C) || defined(PERL_IN_OP_C) || \ defined(PERL_IN_PAD_C) || defined(PERL_IN_PERLY_C) || \ defined(PERL_IN_TOKE_C) diff --git a/embed.h b/embed.h index f6ffdebe92ea..a6c7e19f2bfc 100644 --- a/embed.h +++ b/embed.h @@ -1199,6 +1199,11 @@ # if defined(PERL_IN_AV_C) # define get_aux_mg(a) S_get_aux_mg(aTHX_ a) # endif +# if defined(PERL_IN_BUILTIN_C) || defined(PERL_IN_OP_C) +# define finish_export_lexical() Perl_finish_export_lexical(aTHX) +# define import_builtin_bundle(a,b) Perl_import_builtin_bundle(aTHX_ a,b) +# define prepare_export_lexical() Perl_prepare_export_lexical(aTHX) +# endif # if defined(PERL_IN_CLASS_C) || defined(PERL_IN_GLOBALS_C) || \ defined(PERL_IN_OP_C) || defined(PERL_IN_PEEP_C) # define ck_anoncode(a) Perl_ck_anoncode(aTHX_ a) diff --git a/op.c b/op.c index 9029126ea593..5a37ae2fcbe2 100644 --- a/op.c +++ b/op.c @@ -8046,6 +8046,16 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg) PL_hints &= ~HINT_STRICT_VARS; } + /* As an optimisation, there's no point scanning for changes of + * visible builtin functions when switching between versions earlier + * than v5.39, when any became visible at all + */ + if ((shortver >= SHORTVER(5, 39)) || (PL_prevailing_version >= SHORTVER(5, 39))) { + prepare_export_lexical(); + import_builtin_bundle(shortver, true); + finish_export_lexical(); + } + PL_prevailing_version = shortver; } diff --git a/pod/perlfunc.pod b/pod/perlfunc.pod index 3364c1fd5eb6..bb2106b3f27e 100644 --- a/pod/perlfunc.pod +++ b/pod/perlfunc.pod @@ -10161,12 +10161,18 @@ Compare with L|/require VERSION>, which can do a similar check at run time. If the specified Perl version is 5.12 or higher, strictures are enabled -lexically as with L|strict>. Similarly, if the specified -Perl version is 5.35.0 or higher, L are enabled. Later use of -C will override all behavior of a previous C, -possibly removing the C, C, and C added by it. -C does not load the F, F, or -F files. +lexically as with L|strict>. + +If the specified Perl version is 5.35.0 or higher, L are enabled. + +If the specified Perl version is 5.39.0 or higher, builtin functions are +imported lexically as with L|builtin> with a corresponding +version bundle. + +Later use of C will override all behavior of a previous +C, possibly removing the C, C, C and +C effects added by it. C does not load the +F, F, F or F files. In the current implementation, any explicit use of C or C overrides C, even if it comes before it. diff --git a/pod/perlrun.pod b/pod/perlrun.pod index 6ed5703811d2..7bc264eee17c 100644 --- a/pod/perlrun.pod +++ b/pod/perlrun.pod @@ -470,8 +470,8 @@ to use semicolons where you would in a normal program. X<-E> behaves just like L<-e|/-e commandline>, except that it implicitly -enables all optional features (in the main compilation unit). See -L. +enables all optional features and builtin functions (in the main +compilation unit). See L and L. =item B<-f> X<-f> X X diff --git a/proto.h b/proto.h index 19e3dc632a79..87918bcbc471 100644 --- a/proto.h +++ b/proto.h @@ -6210,6 +6210,23 @@ S_get_aux_mg(pTHX_ AV *av); assert(av) #endif +#if defined(PERL_IN_BUILTIN_C) || defined(PERL_IN_OP_C) +PERL_CALLCONV void +Perl_finish_export_lexical(pTHX) + __attribute__visibility__("hidden"); +# define PERL_ARGS_ASSERT_FINISH_EXPORT_LEXICAL + +PERL_CALLCONV void +Perl_import_builtin_bundle(pTHX_ U16 ver, bool do_unimport) + __attribute__visibility__("hidden"); +# define PERL_ARGS_ASSERT_IMPORT_BUILTIN_BUNDLE + +PERL_CALLCONV void +Perl_prepare_export_lexical(pTHX) + __attribute__visibility__("hidden"); +# define PERL_ARGS_ASSERT_PREPARE_EXPORT_LEXICAL + +#endif /* defined(PERL_IN_BUILTIN_C) || defined(PERL_IN_OP_C) */ #if defined(PERL_IN_CLASS_C) || defined(PERL_IN_GLOBALS_C) || \ defined(PERL_IN_OP_C) || defined(PERL_IN_PEEP_C) PERL_CALLCONV OP * diff --git a/t/comp/use.t b/t/comp/use.t index 8111b6941f5c..e6042f16aef6 100644 --- a/t/comp/use.t +++ b/t/comp/use.t @@ -6,7 +6,7 @@ BEGIN { $INC{"feature.pm"} = 1; # so we don't attempt to load feature.pm } -print "1..85\n"; +print "1..88\n"; # Can't require test.pl, as we're testing the use/require mechanism here. @@ -170,6 +170,18 @@ ok $@, 'no strict vars allows ver decl to enable refs'; eval 'no strict "vars"; use 5.012; ursine_word'; ok $@, 'no strict vars allows ver decl to enable subs'; +# check that "use 5.39.0" and higher imports builtins +{ + my $result; + + $result = eval 'use 5.39.0; my $t = true; $t eq "1"'; + is ($@, "", 'builtin funcs available after use 5.39.0'); + ok ($result, 'imported true is eq "1"'); + + eval 'use 5.39.0; use 5.36.0; my $t = true;'; + like ($@, qr/^Bareword "true" not allowed while "strict subs" in use at /, + 'builtin funcs are removed by use 5.36.0'); +} { use test_use } # check that subparse saves pending tokens diff --git a/t/run/switches.t b/t/run/switches.t index 50318fc32b9b..f72949f2ca6f 100644 --- a/t/run/switches.t +++ b/t/run/switches.t @@ -695,7 +695,12 @@ $TODO = ''; # the -E tests work on VMS $r = runperl( switches => [ '-E', '"say q(Hello, world!)"'] ); -is( $r, "Hello, world!\n", "-E say" ); +is( $r, "Hello, world!\n", "-E enables 'say' feature" ); + +$r = runperl( + switches => [ '-E', '"say reftype []"'] +); +is( $r, "ARRAY\n", "-E enables 'reftype' builtin" ); $r = runperl( switches => [ '-nE', q("} END { say q/affe/") ], diff --git a/toke.c b/toke.c index 9e0e9df1c35e..dd3bbdd0b680 100644 --- a/toke.c +++ b/toke.c @@ -9240,7 +9240,8 @@ yyl_try(pTHX_ char *s) } if (PL_minus_E) sv_catpvs(PL_linestr, - "use feature ':" STRINGIFY(PERL_REVISION) "." STRINGIFY(PERL_VERSION) "';"); + "use feature ':" STRINGIFY(PERL_REVISION) "." STRINGIFY(PERL_VERSION) "'; " + "use builtin ':" STRINGIFY(PERL_REVISION) "." STRINGIFY(PERL_VERSION) "';"); if (PL_minus_n || PL_minus_p) { sv_catpvs(PL_linestr, "LINE: while (<>) {"/*}*/); if (PL_minus_l)