Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tied FETCH unexpectedly invoked twice when returning an object with overload #20574

Closed
leonerd opened this issue Dec 2, 2022 · 4 comments
Closed
Labels
Closable? We might be able to close this ticket, but we need to check with the reporter

Comments

@leonerd
Copy link
Contributor

leonerd commented Dec 2, 2022

The following code unexpectedly invokes the tied FETCH function twice on every iteration line. If instead the code returned a non-overloaded object then this only happens once.

use v5.14;
use warnings;

package BoldStars {
    use overload
        '""' => sub {
            my $self = shift;               ## <-- THIS is line 7
            my $str = $self->[0];
            # Wrap every word in *stars*
            return $str =~ s/(\S+)/*$1*/gr;
        },
        fallback => 1;
    sub new {
        my $class = shift;
        my $self = bless [ $_[0] ], $class;
        return $self;
    }
}

package TiedCounter {
    use Carp;

    my $dashes = "";
    sub TIESCALAR {
        my $class = shift;
        return bless [], $class;
    }
    sub FETCH {
        carp "FETCHing TiedCounter";
        $dashes .= "-";
        return BoldStars->new( $dashes );
    }
}

tie my $tiecounter, "TiedCounter";

say $tiecounter for 1 .. 5;                 ## <-- THIS is line 37

I'd expect it to print 5 rows of output with each one having one more dash than the prior:

*-*
*--*
*---*
*----*
*-----*

but instead it prints:

FETCHing TiedCounter at tie-fetch-twice.pl line 37.
FETCHing TiedCounter at tie-fetch-twice.pl line 7.
*--*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
FETCHing TiedCounter at tie-fetch-twice.pl line 7.
*----*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
FETCHing TiedCounter at tie-fetch-twice.pl line 7.
*------*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
FETCHing TiedCounter at tie-fetch-twice.pl line 7.
*--------*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
FETCHing TiedCounter at tie-fetch-twice.pl line 7.
*----------*

The FETCH function really was run twice for each call. Each row has two more dashes than the prior. The FETCH call from line 37 was expected, the one from lines 7 was most certainly not.

If you remove the use overload ... declaration then this goes away and only runs once (and overloading no longer happens of course):

FETCHing TiedCounter at tie-fetch-twice.pl line 37.
BoldStars=ARRAY(0x556a4784bf70)
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
BoldStars=ARRAY(0x556a4784b4d8)
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
BoldStars=ARRAY(0x556a4784c210)
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
BoldStars=ARRAY(0x556a47858a78)
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
BoldStars=ARRAY(0x556a4781e8c0)
@leonerd
Copy link
Contributor Author

leonerd commented Dec 2, 2022

I should add: This behaviour is seen on any perl from 5.14 to 5.36 and also on current blead.

@demerphq
Copy link
Collaborator

demerphq commented Dec 3, 2022

I dug into this, and have prepared a patch. (breaking on pp_study in gdb helps debugging this.)

Basically the same call to sv_2pv_flags() would trigger the mg_get() from about 5 lines apart. The problem (i think) is that the tied result of the first getmagic is called directly, instead of via a copy of the RV. See #20574.

@jkeenan
Copy link
Contributor

jkeenan commented Dec 6, 2022

@leonerd does #20575, which @demerphq merged four days ago, resolve this problem?

@jkeenan jkeenan added the Closable? We might be able to close this ticket, but we need to check with the reporter label Dec 6, 2022
@leonerd
Copy link
Contributor Author

leonerd commented Dec 6, 2022

Yes; it does - thanks

$ ./perl tie-fetch-twice.pl 
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
*-*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
*--*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
*---*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
*----*
FETCHing TiedCounter at tie-fetch-twice.pl line 37.
*-----*

@leonerd leonerd closed this as completed Dec 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closable? We might be able to close this ticket, but we need to check with the reporter
Projects
None yet
Development

No branches or pull requests

3 participants