Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.85k stars 524 forks source link

h2ph generates bad code, defines symbols in the wrong order #22109

Closed mauke closed 4 weeks ago

mauke commented 1 month ago

Module: h2ph

Description h2ph generates code that uses unless (defined(&FOO)) { sub FOO() { ... } } in various places. This code makes no sense and produces incorrect results in some cases (see below).

Steps to Reproduce Create try.h containing:

#ifndef FOO
    #define FOO 0
#endif

#if FOO == 0
    #define BAR() 1
#else
    #define BAR() 2
#endif

#if BAR() >= 3
    #error "we should never get here"
#endif

#undef BAR
#define BAR() 42

Note that it contains an #error directive that is unreachable because no matter what, BAR() is always defined as either 1 or 2 at this point. Then run:

h2ph -d . try.h
perl -I. -E 'require "try.ph"; say BAR()'

Result:

$ perl -I. -E 'require "try.ph"; say BAR()'
we should never get here at try.ph line 18.
Compilation failed in require at -e line 1.

Expected behavior

42

It fails because the generated try.ph contains:

require '_h2ph_pre.ph';

no warnings qw(redefine misc);

unless(defined(&FOO)) {
    eval 'sub FOO () {0;}' unless defined(&FOO);
}
if((defined(&FOO) ? &FOO : undef) == 0) {
    eval 'sub BAR () {
        eval q(1);
    }' unless defined(&BAR);
} else {
    eval 'sub BAR () {
        eval q(2);
    }' unless defined(&BAR);
}
if( &BAR() >= 3) {
    die("we should never get here");
}
undef(&BAR) if defined(&BAR);
unless(defined(&BAR)) {
    sub BAR () {
    eval q(42);
    }
}
1;

In particular, the unless(defined(&BAR)) { sub BAR () { eval q(42); } } part makes no sense: sub definitions are processed at compile time (like BEGIN blocks), so this code is functionally identical to sub BAR () { eval q(42); } unless(defined(&BAR)) {}. The unless block protects nothing, and the BAR symbol is defined before the rest of the file even starts running.

Similarly, the generated _h2ph_pre.ph file in the same directory contains code like:

unless (defined &_FILE_OFFSET_BITS) { sub _FILE_OFFSET_BITS() { 64 } }

This has the same problem, but at least it doesn't break anything because there are no conditionals or redefinitions here. Still, the unless (defined &X) { } blocks are functionally useless and should be removed.

Perl configuration

Summary of my perl5 (revision 5 version 38 subversion 2) configuration:

  Platform:
    osname=linux
    osvers=6.5.0-10008-tuxedo
    archname=x86_64-linux-thread-multi-ld
    uname='linux luum 6.5.0-10008-tuxedo #8 smp preempt_dynamic mon nov 6 14:48:30 utc 2023 x86_64 x86_64 x86_64 gnulinux '
    config_args='-de -Dprefix=/home/mauke/perl5/perlbrew/perls/perl-5.38.2 -Dcc=cgcc -Dman1dir=none -Dman3dir=none -Dusethreads -Duselongdouble -Aoptimize=-flto -Aeval:scriptdir=/home/mauke/perl5/perlbrew/perls/perl-5.38.2/bin'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=define
    usemultiplicity=define
    use64bitint=define
    use64bitall=define
    uselongdouble=define
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cgcc'
    ccflags ='-D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
    optimize='-O2 -flto'
    cppflags='-D_REENTRANT -D_GNU_SOURCE -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='long double'
    nvsize=16
    Off_t='off_t'
    lseeksize=8
    alignbytes=16
    prototype=define
  Linker and Libraries:
    ld='cgcc'
    ldflags =' -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib /usr/lib64
    libs=-lpthread -ldb -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 -flto -L/usr/local/lib -fstack-protector-strong'

Characteristics of this binary (from libperl): 
  Compile-time options:
    HAS_LONG_DOUBLE
    HAS_STRTOLD
    HAS_TIMES
    MULTIPLICITY
    PERLIO_LAYERS
    PERL_COPY_ON_WRITE
    PERL_DONT_CREATE_GVSV
    PERL_HASH_FUNC_SIPHASH13
    PERL_HASH_USE_SBOX32
    PERL_MALLOC_WRAP
    PERL_OP_PARENT
    PERL_PRESERVE_IVUV
    PERL_USE_SAFE_PUTENV
    USE_64_BIT_ALL
    USE_64_BIT_INT
    USE_ITHREADS
    USE_LARGE_FILES
    USE_LOCALE
    USE_LOCALE_COLLATE
    USE_LOCALE_CTYPE
    USE_LOCALE_NUMERIC
    USE_LOCALE_TIME
    USE_LONG_DOUBLE
    USE_PERLIO
    USE_PERL_ATOF
    USE_REENTRANT_API
    USE_THREAD_SAFE_LOCALE
  Built under linux
  Compiled at Dec  3 2023 20:44:31
  %ENV:
    PERLBREW_BASHRC_VERSION="0.74"
    PERLBREW_HOME="/home/mauke/.perlbrew"
    PERLBREW_MANPATH="/home/mauke/perl5/perlbrew/perls/perl-5.38.2/man"
    PERLBREW_PATH="/home/mauke/perl5/perlbrew/bin:/home/mauke/perl5/perlbrew/perls/perl-5.38.2/bin"
    PERLBREW_PERL="perl-5.38.2"
    PERLBREW_ROOT="/home/mauke/perl5/perlbrew"
    PERLBREW_VERSION="0.94"
    PERLDOC="-oman"
    PERL_UNICODE="SAL"
  @INC:
    /home/mauke/perl5/perlbrew/perls/perl-5.38.2/lib/site_perl/5.38.2/x86_64-linux-thread-multi-ld
    /home/mauke/perl5/perlbrew/perls/perl-5.38.2/lib/site_perl/5.38.2
    /home/mauke/perl5/perlbrew/perls/perl-5.38.2/lib/5.38.2/x86_64-linux-thread-multi-ld
    /home/mauke/perl5/perlbrew/perls/perl-5.38.2/lib/5.38.2