Test case:
use Test::More;
use List::Util 'first';
my %things = (FooBar => 1);
my $first_thing = first { lc $_ eq 'foobar' } keys %things;
is $first_thing, 'FooBar', 'found the right key';
done_testing;
from #p5p:
14:39:43 <Bram> An easy work around would be to use: my @foo = keys %things; ... = first ... @foo; :/
14:40:51 <Grinnz_> huh. that does pass the test
14:41:04 <Grinnz_> so why does @foo work and keys %things not
14:41:41 <Bram> my $first_thing = sort { lc $_ eq "foobar"; } keys %things; also appears to work :/
14:43:14 <hobbs> Grinnz_: keys %things is a list of temps, once they're in an array they're "real"
14:43:23 <Bram> keys %things returns a variable marked as TEMP.. lc on a variable marked as TEMP will do an inplace edit.. If it's in an array it is *not* marked as TEMP so no in place edit is done
14:43:36 <Bram> or what hobbs said.. should've typed faster :)
14:43:55 <Grinnz_> so i guess the next question is, why is it still marked as TEMP when List::Util::first puts it in $_
14:44:26 <hobbs> I think, because multicall aliases it
14:44:46 <hobbs> or, not multicall but really first when it's using multicall
14:44:58 <hobbs> GvSV(PL_defgv) = args[index];
14:45:39 <hobbs> that's pointing $_ to the same SV that came from keys, which is still TEMP
14:47:13 <hobbs> so lc thinks it can modify it, and then first does ST(0) = ST(index) to turn the found item into the return value
14:47:27 <hobbs> but at that point it's been modified
14:47:49 <hobbs> lc thinks that it's the end of the line, if it's receiving a temp than no one else can be using it
14:48:00 <hobbs> but it's wrong here
14:49:00 <Bram> Would it be possible for first to remove the TEMP flag? or will that cause other issues?
14:49:33 <hobbs> I'm not certain, my XS isn't that good, but it seems possible
14:49:45 <hobbs> let me try it
14:49:47 <Bram> Or might be easier: increase the refcount
14:50:14 <Bram> all the code does appear to have '&& SvREFCNT(source) == 1'.. so if increase the ref count (and then decreases it again) it might be fine too
14:50:25 <Bram> no idea what the cleanest/best solution is..
14:51:44 <Grinnz_> for/grep/etc alias the value and don't have the problem, so whatever those are doing?
14:52:52 <hobbs> Bram: yeah, no idea if that's a correct fix, but putting SvREFCNT_inc/dec around the call makes Grinnz_'s test pass
14:54:12 <hobbs>
https://gist.github.com/arodland/be002c0abb6e4048eec9
14:54:13 <dipsy> [ listutil.diff · GitHub ]
14:54:51 <hobbs> again, not claiming that I know what I'm doing, consider it an additional test point rather than a fix :)
14:55:04 <Bram> pp_hot.c:3250 appears to contain: "if (SvPADTMP(src)) { src = PL_stack_base[TOPMARK] = sv_mortalcopy(src);"
-> so I'm guessing grep makes a mortal copy if it sees a TEMP?
14:55:11 <Bram> (assuming that is the correct code)
14:55:36 <hobbs> yeah, pp_grepwhile should be the right place
14:57:21 <hobbs> PADTMP isn't the same thing though
14:58:15 <Bram> hmm, true
14:59:57 <Bram> grep { Dump($_}; ... } keys %hash; shows: 'REFCNT = 2, FLAGS = (POK,IsCOW,pPOK)"; first { Dump($_); ... }shows 'REFCNT = 1, FLAGS = (TEMP,POK,IsCOW,pPOK)' :/
15:00:24 <Bram> oh
15:00:43 <Bram> and the code in pp_grepwhile also has: 'SvTEMP_off(src);' on line 3254
15:00:48 <hobbs> looks like grep unconditionally does SvTEMP_off
15:01:29 <hobbs> which, actually, considering what I was hearing last night about the meaning of that flag, should be a good thing to do
15:02:03 <Bram> so List::Util needs to do it too.. and possible other code on CPAN I guess :/
15:02:18 <hobbs> it doesn't actually make the SV less doomed, it only disables optimizations that assume they can do somethingto it *because* it's doomed
15:04:15 <hobbs>
https://gist.github.com/arodland/833693a99c22db1ec868 also causes a test pass
15:04:15 <dipsy> [ listutil2.diff · GitHub ]