Test-More / Test-More/test-more

Handrolling overloading is done incorrectly.

Open
#879 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Cannot be fixed due to legacy downstream code.
Dominant language
Perl
Stars
149
Forks
98
PR merge metrics
No merged PRs in 30d

Description

Version 1.302175 to Test::Builder contains a serious error when it's trying to mimic Perls overloading.

In particular, the last line (line 728) of the method unoverload. The last two lines are:

    my $string_meth = overload::Method( $$thing, $type ) || return;
    $$thing = $$thing->$string_meth();

Here, it is trying to call the method which is called when overloading is present. (I can't figure out why it's handrolling overloading instead of have perl do it, but that's beside the point). The problem is, it's calling the method without arguments. Yet, according the overload documentation, overload methods are called with multiple arguments, and they aren't optional:

Three arguments are passed to all subroutines specified in the "use
overload" directive (with exceptions - see below, particularly
"nomethod").

The first of these is the operand providing the overloaded operator
implementation - in this case, the object whose "minus()" method is
being called.

The second argument is the other operand, or "undef" in the case of a
unary operator.

The third argument is set to TRUE if (and only if) the two operands have
been swapped. Perl may do this to ensure that the first argument ($self)
is an object implementing the overloaded operation, in line with general
object calling conventions.

And for some overload methods, there are even a fourth and fifth argument.

Not passing in mandatory arguments is a problem for code which uses subroutine signatures.

Here is a short program which exhibits the problem:

#!/usr/bin/perl

use 5.028;

use strict;
use warnings;

use experimental 'lexical_subs';

use Test::More;

package Test {
    use overload '""'     => \&stringify,
                 fallback => 1;
    sub stringify ($self, $, $) {
        $$self
    }
}

my $x = bless \do {my $var = 3} => "Test";
say       $x;                     # Fine
is        $x, 3, "Test 1";        # Also fine
is_deeply $x, 3, "Test 2";        # Fails

done_testing;

__END__

Running this gives:

3
ok 1 - Test 1
Too few arguments for subroutine 'Test::stringify' at /usr/lib/pakket/5.28.1/libraries/active/lib/perl5/Test/Builder.pm line 728.

In the call to say, and the call to is, perl calls the overload method, and calls it with three arguments. In the call to is_deeply, it is Test::Builder which calls it, and calls with missing arguments.

Note that using a signature of ($self) isn't going to work, as that will trigger a Too many arguments... error when it's perl who does the overloading.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Test::Builder.pm at the unoverload method around line 728, then run the reproducer from the issue with a signature-based overload method. Done means the is_deeply call succeeds without missing-argument errors, while the existing say and is cases continue to pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
perl
Domain
testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.