in reply to Re: UPDATED, mostly solved: separation of define + assignment different from combination?
in thread UPDATED, mostly solved: separation of define + assignment different from combination?

There is no bug, everything is working as it should, warnings is doing its job
use warnings; sub FUNC{ print "I'm in func\n"; } { local(*FUNC) = sub { print "now overridden\n"; }; FUNC(); ## THIS IS LINE 6 } FUNC(); __END__ Subroutine main::FUNC redefined at - line 6. now overridden I'm in func
  • Comment on Re^2: UPDATED, mostly solved: separation of define + assignment different from combination?
  • Download Code

Replies are listed 'Best First'.
Re^3: UPDATED, mostly solved: separation of define + assignment different from combination?
by choroba (Cardinal) on Feb 12, 2014 at 13:20 UTC
    Especially the comment "THIS IS LINE 6" on line 7 is nice.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      probably meant "# WILL EXECUTE LINE SIX"
Re^3: UPDATED, mostly solved: separation of define + assignment different from combination?
by andal (Hermit) on Feb 13, 2014 at 07:45 UTC
    There is no bug, everything is working as it should, warnings is doing its job

    Well, first of all you made mistake, the line 6 is where the assignment is done. Second, if you separate the assignment from localizing, then no warning is produced. According to documentation, there should be no difference. Whether one writes

    local(*FUNC) = sub {};
    or
    local(*FUNC); *FUNC = sub {};
    things should be the same. But they are not. First one produces warning, second one - not. That is why I said, that it might be considered a bug.

      Well, first of all you made mistake, the line 6 is where the assignment is done.

      :)

      Second, if you separate the assignment from localizing, then no warning is produced.

      um, put that in the code?

      use warnings; sub RO { warn "RO" } sub SHAM { warn "SHAM" } RO(); { local *RO = sub { warn "BO" }; RO(); } SHAM(); { local *SHAM; *SHAM = sub { warn "BO" }; SHAM(); } RO(); SHAM(); __END__ RO at - line 2. Subroutine main::RO redefined at - line 5. BO at - line 5. SHAM at - line 3. BO at - line 7. RO at - line 2. SHAM at - line 3.

      So, that kinda makes sense to me, when its redefined, you get warning

      When its undefined, then its defined , you get no warning

      So , to see if its a bug , I look at what warnings says which is

      Subroutine %s redefined (W redefine) You redefined a subroutine. To suppress this warning, say { no warnings 'redefine'; eval "sub name { ... }"; }

      So my new code snippet is

      $ perl -le " use warnings; sub foo { warn 1 } foo(); local *foo; sub f +oo { warn 2 } foo(); " Subroutine foo redefined at -e line 1. 2 at -e line 1. Undefined subroutine &main::foo called at -e line 1.

      Hmm, at compile time its redefined, but at run time its undefined

      Oh right, eval

      $ perl -le " use warnings; sub foo { warn 1 } foo(); local *foo; eval +q{sub foo { warn 2 }}; foo(); " 1 at -e line 1. 2 at (eval 5) line 1.

      no redefined warning is given

      And third take round trip

      $ perl -le " use warnings; sub foo { warn 1 } foo(); { local *foo; eva +l q{sub foo { warn 2 }}; foo(); } foo(); " 1 at -e line 1. 2 at (eval 5) line 1. 1 at -e line 1.

      no redefined warning is given

      So , yup, the situation makes sense from a technical point, at the time the new sub is defined, the old sub doesn't exist in the current scope -- current stash -- it doesn't exist

      Could perl/warnings be made to detect this case? Sure

      Would it be worth it? I doubt it -- its essentially double checking

      So, I don't see it as a bug or undesireable; I don't see a compelling reason to change it

      Cheers

        So as soon as I posted that, I thought surely there should be some kind of critic policy, and before checking first type, Perl::Critic::Policy::Warnings::SubroutineRedefinedUndefinedDefined

        After checking I find three relevant ones
        Perl::Critic::Policy::Variables::RequireInitializationForLocalVars - Use my instead of local, except when you have to.
        Perl::Critic::Policy::Variables::ProhibitLocalVars - Write local $foo = $bar; instead of just local $foo;

        So my new candidate is
        Perl::Critic::Policy::Subroutines::ProhibitSeperatedRedefinition - prohibit that which gets by warnings  sub foo { ... } local *foo; *foo = sub { };

        Should be fairly trivial to write since Perl::Critic::Policy is xpaths

        This train of thought firmly establishes in my mind that this situation isn't warnings territory

Re^3: (updated comment)UPDATED, mostly solved: separation of define + assignment different from combination?
by perl-diddler (Chaplain) on Feb 12, 2014 at 22:11 UTC
    The above doesn't really reproduce the error as 'FUNC' was in a 'sub' that got called recursively (which triggered the warning).

    Also, in that example, it is hard to see when the warning was 'issued', as STDERR is not buffered while STDOUT is, so the warning(on STDERR) would be seen (in this case), before any output from STDOUT (flushed @ program exit).

    I've rewritten the code below multiple times to experiment w/outcome. This reproduces the problem, and shows when an object containing the 'local' gets destroyed. There are two counters printed -- a "singleton" counter (1 for package), and a local copy of the global when the object was created (cuz they aren't the same on destruction). Hope this doesn't give anyone a headache like it did me in writing it...;-) Minor change w/change-line to allow breaking define+assign (i.e. via the '#' in column 2 (before the code) -- removing or not gets non-split vs. split define+assign

    #!/usr/bin/perl use warnings; use strict; use P; { package pkg; use warnings; use strict; use P; use Types::Core; our $cnt=0; sub new { my $p = shift; my $c = ref $p || $p; Pe "pkg %s created", ++$cnt; $p = bless { cnt => $cnt, fnc => sub { my $p2 = ref $_[0] ? shift : __PACKAGE__; Pe "fnc %d calling %s", $cnt, $p2->FUNC; undef; } }, $c; } sub destroy { Pe "pkg %s(%s) destroyed", $cnt, EhV $_[0], cnt; undef + } sub DESTROY { goto &destroy } } package main; sub callfunc(;$$); #silence warn about proto not ready sub callfunc (;$$) { my $p = pkg->new; my ($callfunc, $recur) = @_; $callfunc||=0; $recur||=0; local * FUNC #; *FUNC # change line = sub () { Pe "In FUNC, cnt=%s", $p->{cnt}; undef }; FUNC() if $callfunc; callfunc($callfunc, $recur) if $recur && $recur--; } Pe "** no recursion"; callfunc; callfunc 1; Pe "** split define/assign recursion"; callfunc 0,1; callfunc 1,1; pkg::destroy
    ---output for non-split:---
    ** no recursion pkg 1 created pkg 1(1) destroyed pkg 2 created In FUNC, cnt=2 pkg 2(2) destroyed ** split define/assign recursion pkg 3 created pkg 4 created Subroutine main::FUNC redefined at /tmp/tst2.pl line 29. pkg 4(4) destroyed pkg 4(3) destroyed pkg 5 created In FUNC, cnt=5 pkg 6 created Subroutine main::FUNC redefined at /tmp/tst2.pl line 29. In FUNC, cnt=6 pkg 6(6) destroyed pkg 6(5) destroyed pkg 6(∄) destroyed
    ---output for split case ---
    ** no recursion pkg 1 created pkg 1(1) destroyed pkg 2 created In FUNC, cnt=2 pkg 2(2) destroyed ** split define/assign recursion pkg 3 created pkg 4 created pkg 4(4) destroyed pkg 4(3) destroyed pkg 5 created In FUNC, cnt=5 pkg 6 created In FUNC, cnt=6 pkg 6(6) destroyed pkg 6(5) destroyed pkg 6(∄) destroyed
    I used Types::Core's EhV, to allow dereferencing $_[0] and returning the value of $_[0] in 1 step. I used P's 'Pe' instead of 'P' to print to STDERR so my output would be unbuffered and intermixed with perl's STDERR stream.

    Side note: In case your local font doesn't display the default 'undef' sign used by P, (present in parens on the last line of output), it looks like 'E' with a diagonal crossout through it, but backward (in case your font doesn't display it).

    As the output shows above, the warning message only occurs on recursive calls to the sub where the local defines the sub (and breaking the local into a separate declaration followed by the assignment doesn't show the problem....ah crud.. didn't put that in the example -- **UPDATED**

    Now we can see that the recursive value gets destroyed before returning so no overwrite is detected...

    p.s. I note the <code> function is broken -- it doesn't post output "as is", but turns it into non-standard, decimal HTML (i.e. &&;#8708 is a decimal number, and current standards default to hex as in "&&;#x2204;" ).

    p.p.s. -- I note that while the <code> doesn't maintain literal values in displaying code, these comments do, so if your charset has the character it should be between the quotes: "∄". Cheers!

      ... use P; use Types::Core; ...

      Sorry , but tThat is not a minimal demonstration of a bug in perl, something you should have included in the OP

        From OP:
        This is no 'deep' subject or problem (that I know of), but it sorta surprised me.
        Then you write:
        ... use P; use Types::Core; ... Sorry , but tThat is not a minimal demonstration of a bug in perl, something you should have included in the OP

        I don't know, but it sure seems like the Anon posting function being abused by someone trolling and looking to create a toxic atmosphere. --- BTW -- both of those modules are relatively simple and in pure perl.