Perl / perl5

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

Importing a function from a module which does not export does not throw an error #19417

Open demerphq opened 2 years ago

demerphq commented 2 years ago

Description Using a module which does not export, such as a class, which does not export does not throw an exception.

Steps to Reproduce perl -le'use File::Spec qw(catfile);'

Expected behavior Expecting some kind of error which shows that the request function or symbol has not actually been exported.

Perl configuration

perl -V
Summary of my perl5 (revision 5 version 24 subversion 1) configuration:

  Platform:
    osname=linux, osvers=3.19.0-32-generic, archname=x86_64-linux
    uname='linux styx2 3.19.0-32-generic #37~14.04.1-ubuntu smp thu oct 22 09:41:40 utc 2015 x86_64 x86_64 x86_64 gnulinux '
    config_args='-de -Dcc=ccache gcc -Dld=gcc -Dprefix=/home/yorton/perl5/perlbrew/perls/perl-5.24.1 -Aeval:scriptdir=/home/yorton/perl5/perlbrew/perls/perl-5.24.1/bin'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='ccache gcc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2',
    cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
    ccversion='', gccversion='4.8.4', 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='gcc', ldflags =' -fstack-protector -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib
    libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.19.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.19'
  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'

Characteristics of this binary (from libperl): 
  Compile-time options: HAS_TIMES PERLIO_LAYERS PERL_COPY_ON_WRITE
                        PERL_DONT_CREATE_GVSV
                        PERL_HASH_FUNC_ONE_AT_A_TIME_HARD PERL_MALLOC_WRAP
                        PERL_PRESERVE_IVUV USE_64_BIT_ALL USE_64_BIT_INT
                        USE_LARGE_FILES USE_LOCALE USE_LOCALE_COLLATE
                        USE_LOCALE_CTYPE USE_LOCALE_NUMERIC USE_LOCALE_TIME
                        USE_PERLIO USE_PERL_ATOF
  Locally applied patches:
    Devel::PatchPerl 1.38
  Built under linux
  Compiled at Apr 11 2017 12:40:00
  %ENV:
    PERLBREW_CONFIGURE_FLAGS="-de -Dcc=ccache\ gcc -Dld=gcc"
    PERLBREW_HOME="/home/yorton/.perlbrew"
    PERLBREW_MANPATH="/home/yorton/perl5/perlbrew/perls/perl-5.24.1/man"
    PERLBREW_PATH="/home/yorton/perl5/perlbrew/bin:/home/yorton/perl5/perlbrew/perls/perl-5.24.1/bin"
    PERLBREW_PERL="perl-5.24.1"
    PERLBREW_ROOT="/home/yorton/perl5/perlbrew"
    PERLBREW_SHELLRC_VERSION="0.88"
    PERLBREW_VERSION="0.88"
  @INC:
    /home/yorton/perl5/perlbrew/perls/perl-5.24.1/lib/site_perl/5.24.1/x86_64-linux
    /home/yorton/perl5/perlbrew/perls/perl-5.24.1/lib/site_perl/5.24.1
    /home/yorton/perl5/perlbrew/perls/perl-5.24.1/lib/5.24.1/x86_64-linux
    /home/yorton/perl5/perlbrew/perls/perl-5.24.1/lib/5.24.1
    .
Grinnz commented 2 years ago

This isn't a bug. Importing symbols is only one of infinite possibilities that may occur when passing arguments to import. At most we could introduce a warning when passing import arguments and there is no import method.

demerphq commented 2 years ago

On Mon, 14 Feb 2022 at 16:12, Dan Book @.***> wrote:

This isn't a bug.

Where is that documented?

Importing symbols is only one of infinite possibilities that may occur when passing arguments to import. At most we could introduce a warning when passing import arguments and there is no import method.

When I said "which does not export" I meant "and there is no import method".

I do not agree all we can do is throw a warning, unless this is documented behavior we can do what we choose.

I may happen to agree that a warning would be less traumatic, but I do not agree we have no choice. Absent documentation we can decide what is a bug or not and what to do about it.

cheers, Yves

Grinnz commented 2 years ago

I mean it is not a bug because import arguments can have any number of functions, and any behavior based on whether or not symbols were exported is a logical error. e.g. it is perfectly reasonable for "use Foo 'bar'" to do some random bar behavior and not import anything called 'bar'. Behavior based on whether or not an import method exists is possible, such as the warning idea discussed elsewhere.

demerphq commented 2 years ago

On Wed, 16 Feb 2022 at 03:17, Dan Book @.***> wrote:

I mean it is not a bug because import arguments can have any number of functions, and any behavior based on whether or not symbols were exported is a logical error.

I dont understand why you are bringing up symbols here. The title of this ticket is "Importing a function from a module which does not export does not throw an error". Perhaps I should have written a more artful title line for the issue, but in my previous reply I already explained that I meant in the case where there is no import method at all.

The concern I have is that passing arguments into a non-existent import method should be an error of some kind. (I don't mind if it is a warning or a fatal exception.)

The reason for this is that it would allow us to catch real bugs and save head scratching for beginners and for experts, for instance the case insensitive file system scenario mentioned in another ticket:

use List::util qw(sum);

Will silently "work" on a mac or windows box (meaning it wont throw an exception about not finding "List/util.pm", and the List::Util package will be loaded into memory), but it will not import "sum". Which then makes people confused. I think as much as possible our rules shouldn't leave devs confused if we can help it.

I also think things like the following are problematic:

use File::Spec qw(catfile);

which makes it looks like you are importing "catfile" but are actually not as File::Util is a class module and does not have an import method. A beginner would see that and think they can write:

catfile("a","b","c");

and then wonder why it doesn't work. There is even exactly such a bogus 'import' in core right now. There is also a case in Porting/GitUtils.pm and Porting/make_dot_patch.pl where the module is used like this:

use GitUtils qw(gen_dot_patch);

but Porting/GitUtils.pm lacks a package declaration, and the code involved only "works" because Porting/make_dot_patch.pl does not declare itself to be in any particular package so it executes in package main. Because of the missing package declaration in Porting/GitUtils.pm the sub "gen_dot_patch" is installed into main, so the script does work, but not for the reasons I expected and intended, and I wrote the module and the code that uses it. I definitely didn't intend to be tricky there. I would say this is surprising and confusing.

Making such things produce some kind of exception would eliminate surprise and confusion. Eg, if the script that contains the

use GitUtils qw(gen_dot_patch);

line added its own package declaration would you expect the code to break? I wouldn't expect a properly written plain vanilla script to change behavior simply because I decided to move it from package main to package Foo.

I can change the ticket headline if you find that helpful, but this ticket has nothing to do with what an import() method chooses to do with an argument, and everything to do with Perl having some sloppy special casing for missing import()/unimport() methods which then leads to surprises and head-scratching and embarrassment.

The GitUtils bug has been in place since 2009, if a relatively expert perl hacker like myself can do this kind of accidentally working mistake in core perl and nobody working on core notices it for 12 years then imagine the kind of confusions beginners can create for themselves.

cheers, Yves