kcott has asked for the wisdom of the Perl Monks concerning the following question:

I encountered a problem when using the if pragma and modules with an empty import list. I suspect this is a bug or, at the very least, a limitation which should be documented. Before I raise a bug report, any feedback on this issue would be welcome (can you reproduce it? have I missed something? anything else?). To demonstrate, here's some examples using warnings for the module.

Firstly, results with no list and an empty list are as expected:

$ perl -Mstrict -E 'use warnings; my $x; say $x+1' Use of uninitialized value $x in addition (+) at -e line 1. 1 $ perl -Mstrict -E 'use warnings (); my $x; say $x+1' 1

Adding the if pragma into the mix, and the empty list acts like no list:

$ perl -Mstrict -E 'use if 1==1, warnings => (); my $x; say $x+1' Use of uninitialized value $x in addition (+) at -e line 1. 1

Of course, a workaround is straightforward (but not what I'm looking for):

$ perl -Mstrict -E 'BEGIN { require warnings if 1==1 } my $x; say $x+1 +' 1

Running the code through B::Deparse shows the syntax is fine; however, using the code it produces doesn't resolve the problem:

$ perl -MO=Deparse,-p -e 'use if 1==1, warnings => ()' use if (1, 'warnings', ()); -e syntax OK $ perl -Mstrict -E 'use if (1, "warnings", ()); my $x; say $x+1' Use of uninitialized value $x in addition (+) at -e line 1. 1

I've used warnings because it was handy for illustrating the problem (I don't actually have any real code with 'use warnings ();'). Here's a few more examples, all of which abort compilation when the if pragma is used.

$ perl -wMstrict -E 'use open ()' $ perl -wMstrict -E 'use if 1==1, open => ()' open: needs explicit list of PerlIO layers at -e line 1. BEGIN failed--compilation aborted at -e line 1. $ perl -wMstrict -E 'use feature ()' $ perl -wMstrict -E 'use if 1==1, feature => ()' No features specified at -e line 1. BEGIN failed--compilation aborted at -e line 1. $ perl -wMstrict -E 'use List::Util ()' $ perl -wMstrict -E 'use if 1==1, List::Util => ()' Bareword "List::Util" not allowed while "strict subs" in use at -e lin +e 1. Execution of -e aborted due to compilation errors.

I've looked at the source code for the if pragma. It's actually fairly short (without the POD) so I've posted it here for reference. This version (0.0606) is what's used in the latest production (5.24.1) and development (5.25.11) Perls.

package if; $VERSION = '0.0606'; sub work { my $method = shift() ? 'import' : 'unimport'; unless (@_ >= 2) { my $type = ($method eq 'import') ? 'use' : 'no'; die "Too few arguments to '$type if' (some code returning an empty + list in list context?)" } return unless shift; # CONDITION my $p = $_[0]; # PACKAGE (my $file = "$p.pm") =~ s!::!/!g; require $file; # Works even if $_[0] is a keyword (like open) my $m = $p->can($method); goto &$m if $m; } sub import { shift; unshift @_, 1; goto &work } sub unimport { shift; unshift @_, 0; goto &work } 1;

As you can see, goto &NAME is used in all subroutines. I'm fairly sure that the empty import list gets lost in 'goto &work' because '@_' gets flattened:

$ perl -wMstrict -E '@_ = (1, 2, ()); say 0+@_' 2

This results in all statements like this:

use if CONDITION, MODULE => ();

effectively becoming this:

use if CONDITION, MODULE;

without MODULE being quoted.

— Ken

Replies are listed 'Best First'.
Re: 'if' pragma and modules with an empty import list
by haukex (Archbishop) on Apr 11, 2017 at 06:57 UTC

    I think the thing is that use Module (); is a unique case, as the docs highlight: "Again, there is a distinction between omitting LIST (import called with no arguments) and an explicit empty LIST () (import not called)." I think that the LIST in use Module VERSION LIST; is passed to the Module->import(LIST) method using Perl's normal argument list handling, meaning that empty lists will be flattened. This includes everything after the "if" in use if CONDITION, MODULE => ARGUMENTS;.

    $ cat Foo.pm package Foo; use warnings; use strict; use Data::Dump 'pp'; our $VERSION = 1.23; sub import { print "import ".pp(\@_)."\n" } 1; $ perl -wMstrict -e 'use Foo ()' $ perl -wMstrict -e 'use Foo' import ["Foo"] $ perl -wMstrict -e 'use Foo "bar"' import ["Foo", "bar"] $ perl -wMstrict -e 'use Foo 1.20 "bar"' import ["Foo", "bar"] $ perl -wMstrict -e 'use Foo "bar", "quz"' import ["Foo", "bar", "quz"] $ perl -wMstrict -e 'use Foo "bar", ()' import ["Foo", "bar"] $ perl -wMstrict -e 'use Foo "bar", (), "quz"' import ["Foo", "bar", "quz"] $ perl -wMstrict -e 'use Foo "CONDITION", "MODULE" => "ARGUMENTS"' import ["Foo", "CONDITION", "MODULE", "ARGUMENTS"] $ perl -wMstrict -e 'use Foo "CONDITION", "MODULE"' import ["Foo", "CONDITION", "MODULE"] $ perl -wMstrict -e 'use Foo "CONDITION", "MODULE" => ()' import ["Foo", "CONDITION", "MODULE"]

    So if's behavior isn't really surprising, but I agree that its docs could mention this. As ikegami said, it's the perfect chance to get a patch in :-)

    Personally I wouldn't mind the workaround BEGIN { require Module if CONDITION }, but you're right about the doc issue.

      G'day haukex,

      Take a look at ++beech's detailed response.

      The patch has been sent. perlbug is telling me: "... automated response acknowledging your message within a few hours ...". I'll post the ticket number when I have it.

      — Ken

Re: 'if' pragma and modules with an empty import list
by beech (Parson) on Apr 11, 2017 at 04:04 UTC

    Hi

    BEGIN { package G; $INC{q/G.pm/}=__FILE__; sub import { warn qq{@_ }; } } use G(); use G; use G; __END__ G at - line 1. G at - line 1.

    As you can see import is only called twice when the empty parens aren't present

    So if.pm does not know that the user didnt want import called

    It might be possible to determine that using B::Deparse but that seems a bit much for a pragma

      G'day beech,

      "As you can see import is only called twice when the empty parens aren't present"

      I may be missing the point your trying to make here. I would only expect import() to be called twice (once for each 'use G;'). The empty list is intended to stop import() from being called.

      From use:

      If you do not want to call the package's import method (for instance, to stop your namespace from being altered), explicitly supply the empty list:

      use Module ();

      Although the arguments to use if are (CONDITION, MODULE, ()) — see the 'perl -MO=Deparse ...' line from my OP — I'm resonably sure the third argument (the empty list) is lost due to @_ being flattened; the version of @_, used by goto &work in if::import(), no longer has that empty list.

      Anyway, as I said, I think I may be missing some point you're making. If you'd care to clarify, I'd be happy to discuss further.

      — Ken

        Anyway, as I said, I think I may be missing some point you're making. If you'd care to clarify, I'd be happy to discuss further.

        Hi

        My point is that empty list , the parens , are not being lost, they never existed, and they can't be passed around, because foo() is the same as foo((),(),())

        Its a special case in the parser that causes use YADA (); to be treated as BEGIN{require YADA;} with no call to import.

        B::Deparse peeks at the optree to decide when to print use YADA();

        This is the parsing parts of a "use" statement

        https://perl5.git.perl.org/perl.git/blob/HEAD:/perly.y#l349

        349 | USE startsub 350 { CvSPECIAL_on(PL_compcv); /* It's a BEGI +N {} */ } 351 BAREWORD BAREWORD optlistexpr ';' 352 { 353 SvREFCNT_inc_simple_void(PL_compcv); 354 utilize($1, $2, $4, $5, $6); 355 parser->parsed_sub = 1; 356 $$ = NULL; 357 }

        toke.c s_tokenize_use

        Related loading module parts
        op.c Perl_vload_module
        op.c Perl_utilize

        6307 /* Fake up an import/unimport */ 6308 if (arg && arg->op_type == OP_STUB) { 6309 imop = arg; /* no import on explicit () */ 6310 }

        http://perldoc.perl.org/perlapi.html#load_module

        If you look inside sub begin_is_use inside B::Deparse ( L571 and L611 ) you can see what it goes through to decide if its dealing with a use statement with or without empty parens, its looking at the optree, because of the fake begin sub in utilize

        As you can see, the empty parens simply never existed

        Similar to my post above, but a weaker demonstration, if you look at perlrun there are two switches for loading modules

        $ perl -mopen -e 1 $ perl -Mopen -e 1 open: needs explicit list of PerlIO layers at -e line 0. BEGIN failed--compilation aborted.

        Passing no arguments (like empty parens) doesn't work with either version import gets called , because in the end its a simple use statement

        $ perl -Mopen= -e 1 open: needs explicit list of PerlIO layers at -e line 0. BEGIN failed--compilation aborted. $ perl -mopen= -e 1 open: needs explicit list of PerlIO layers at -e line 0. BEGIN failed--compilation aborted.
Re: 'if' pragma and modules with an empty import list
by ikegami (Patriarch) on Apr 11, 2017 at 05:58 UTC

    Well, it does say it uses use MODULE ARGUMENTS; when it's specifically the use MODULE (); syntax that prevents a module's import from being called. Clarification could be helpful, but it's not a bug.

    Don't just submit a ticket pointing clarification could be useful. At a minimum, provide a recommended alternative.

      G'day ikegami,

      Thanks for the feedback.

      It does also talk about no ARGUMENTS but, you are correct, ARGUMENTS being an empty list is not mentioned.

      The BUGS section already says it doesn't handle VERSION. I was going to suggest adding "empty list" to that.

      "At a minimum, provide a recommended alternative."

      In my OP, I proposed a workaround along the lines of:

      BEGIN { require MODULE if CONDITION }

      I'll assume that's adequate; however, if you had something more substantial in mind, please advise. Thanks.

      — Ken

        In my OP, I proposed a workaround along the lines of:

        I meant: Please provide *specific* wording changes (if not a patch).

Re: 'if' pragma and modules with an empty import list [patch submitted]
by kcott (Archbishop) on Apr 13, 2017 at 03:35 UTC

    Just a quick update to advise that the patch has been submitted: #131142.

    Thanks again to all who provided feedback.

    — Ken