Description
This is a bug report for perl, generated with the help of perlbug 1.43 running under perl 5.39.3.
Using a self-modifying tied variable as the separator in join
gives unexpected results.
Description
While implementing PPC 0013, I've been exploring the behaviour of join
with overloaded separator and arguments, and also with tied separator and arguments.
This case is about using a self-modifying (in FETCH
) tied variable both as a separator and multiple times in the argument list.
Expected behavior
The following (passing) test case sets expectations:
FETCH
is called once on a tied separator, and the returned string is used to join the arguments- a
join
with a single argument detected at compilation is replaced with anOP_STRINGIFY
, soFETCH
is never called - when the tied separator is also used in the argument list,
FETCH
is called once to get the separating string, and once for each occurence of the variable in the argument list
use Test::More;
# simple tied variable
{
package S;
our $fetched;
sub TIESCALAR { my $x = '-'; $fetched = 0; bless \$x }
sub FETCH { my $y = shift; $fetched++; $$y }
package main;
my $t;
tie $t, 'S';
is( join( $t, a .. c ), 'a-b-c', 'tied separator' );
is( $S::fetched, 1, 'FETCH called once' );
tie $t, 'S';
is( join( $t, 'a' ), 'a', 'tied separator on single item join' );
is( $S::fetched, 0, 'FETCH not called' );
tie $t, 'S';
is( join( $t, 'a', $t, 'b', $t, 'c' ),
'a---b---c', 'tied separator also in the join arguments' );
is( $S::fetched, 3, 'FETCH called 1 + 2 times' );
}
done_testing;
Steps to Reproduce
When the tied variable is self-modifying in FETCH
, the result does not seem to follow the above expectations:
use Test::More;
# self-modifying tied variable
{
package SM;
our $fetched;
sub TIESCALAR { my $x = "1"; $fetched = 0; bless \$x }
sub FETCH { my $y = shift; $fetched++; $$y += 3 }
package main;
my $t;
tie $t, "SM";
is( join( $t, a .. c ), 'a4b4c', 'tied separator' );
is( $SM::fetched, 1, 'FETCH called once' );
tie $t, "SM";
is( join( $t, 'a' ), 'a', 'tied separator on single item join' );
is( $SM::fetched, 0, 'FETCH not called' );
tie $t, "SM";
is( join( $t, "a", $t, "b", $t, "c" ),
'a474b4104c', 'tied separator also in the join arguments' );
is( $SM::fetched, 3, 'FETCH called 1 + 2 times' );
}
done_testing;
In the last call to join
, FETCH
is called the expected number of times, but the result is not what we expect, based on the behaviour described in the first case:
# expected: 'a474b4104c'
^ separator (FETCH)
^ tied argument (FETCH)
^ separator (cached)
^ separator (cached)
^ tied argument (FETCH)
^ separator (cached)
versus:
# got: 'a477b7101c'
^ separator (FETCH)
^ tied argument (FETCH)
^ ??? separator (previously fetched value?)
^ ??? separator (same as before?)
^ tied argument (FETCH)
^ ???
Flags
- category=core
- severity=medium
Perl configuration
Site configuration information for perl 5.39.3:
Configured by book at Wed Sep 6 08:18:57 CEST 2023.
Summary of my perl5 (revision 5 version 39 subversion 3) configuration:
Commit id: 008ae6a0ef7f87df8ee75ca523c6adf1ae0b768d
Platform:
osname=linux
osvers=6.2.0-31-generic
archname=x86_64-linux
uname='linux zlopp 6.2.0-31-generic #31~22.04.1-ubuntu smp preempt_dynamic wed aug 16 13:45:26 utc 2 x86_64 x86_64 x86_64 gnulinux '
config_args='-des -Dusedevel'
hint=recommended
useposix=true
d_sigaction=define
useithreads=undef
usemultiplicity=undef
use64bitint=define
use64bitall=define
uselongdouble=undef
usemymalloc=n
default_inc_excludes_dot=define
Compiler:
cc='cc'
ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
optimize='-O2'
cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
ccversion=''
gccversion='11.4.0'
gccosandvers=''
intsize=4
longsize=8
ptrsize=8
doublesize=8
byteorder=12345678
doublekind=3
d_longlong=define
longlongsize=8
d_longdbl=define
longdblsize=16
longdblkind=3
ivtype='long'
ivsize=8
nvtype='double'
nvsize=8
Off_t='off_t'
lseeksize=8
alignbytes=8
prototype=define
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib /usr/lib64
libs=-lpthread -ldl -lm -lcrypt -lutil -lc
perllibs=-lpthread -ldl -lm -lcrypt -lutil -lc
libc=/lib/x86_64-linux-gnu/libc.so.6
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.35'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
---
@INC for perl 5.39.3:
lib
/usr/local/lib/perl5/site_perl/5.39.3/x86_64-linux
/usr/local/lib/perl5/site_perl/5.39.3
/usr/local/lib/perl5/5.39.3/x86_64-linux
/usr/local/lib/perl5/5.39.3
---
Environment for perl 5.39.3:
HOME=/home/book
LANG=en_US.UTF-8
LANGUAGE (unset)
LC_ADDRESS=fr_FR.UTF-8
LC_IDENTIFICATION=fr_FR.UTF-8
LC_MEASUREMENT=fr_FR.UTF-8
LC_MONETARY=fr_FR.UTF-8
LC_NAME=fr_FR.UTF-8
LC_NUMERIC=fr_FR.UTF-8
LC_PAPER=fr_FR.UTF-8
LC_TELEPHONE=fr_FR.UTF-8
LC_TIME=fr_FR.UTF-8
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=/home/book/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
PERL_BADLANG (unset)
SHELL=/bin/bash