rakudo icon indicating copy to clipboard operation
rakudo copied to clipboard

Raku Signature not able to generate a method signature

Open wayland opened this issue 1 year ago • 1 comments

The Problem

A manually constructed Signature has no way of matching a method Signature. One problem is likely at https://github.com/rakudo/rakudo/blob/main/src/core.c/Signature.rakumod#L128 -- this should compare the types, but the two return parameters shouldn't have to be the exact same parameter -- being the same type should be sufficient.

Steps to Reproduce

Use the following code to demonstrate a number of bugs -- test 5 is the focus of this issue, but tests 3 and 4 aren't 100% either.

#!/usr/bin/raku

#

sub     compare-signatures($name, $expectation, $sig1, $sig2, $problem) {
        say "$name";
        say "\tExpectation: $expectation";
        say "\tsig1.ACCEPTS(sig2): {$sig1.ACCEPTS($sig2)}";
#       if $sig1.ACCEPTS($sig2).Str eq $expectation { return; }
        say "\tStraight comparison";
        say "\t\tsig1: {$sig1.raku}";
        say "\t\tsig2: {$sig2.raku}";
        say "\tarity: {$sig1.arity} vs. {$sig2.arity}";
        say "\tcount: {$sig1.count} vs. {$sig2.count}";
        say "\treturns.WHICH: {$sig1.returns.WHICH} vs. {$sig2.returns.WHICH} = {$sig1.returns =:= $sig2.returns}";
        say "\treturns.raku: {$sig1.returns.raku} vs. {$sig2.returns.raku} = {$sig1.returns =:= $sig2.returns}";
        if $problem {
                say "\tProblem: $problem";
        }
        say '';
}

class   A {
        method  foo(Str $a, Int $b) {}
}

my $class_a = A.new();

# Signatures material
my $signature-method = A.^lookup('foo').signature;

my @signature-tests;

@signature-tests.push: [
        1,
        'False',
        :(Str $a, Int $b, *%_),
];
@signature-tests.push: [
        2,
        'True',
        :(A: Str $a, Int $b, *%_)
];
@signature-tests.push: [
        3,
        'True',
        Signature.new(
                params => (
                        Parameter.new(
                                type => 'A',
                                invocant => True,
                        ),
                        Parameter.new(
                                name => '$a',
                                type => 'Str',
                        ),
                        Parameter.new(
                                name => '$b',
                                type => 'Int',
                        ),
                        Parameter.new(
                                name => '*%_',
                        ),
                ),
        ),
        'Arity and count should sort themselves out a little better, but not a big problem',
];
@signature-tests.push: [
        4,
        'True',
        Signature.new(
                params => (
                        Parameter.new(
                                type => 'A',
                                invocant => True,
                        ),
                        Parameter.new(
                                name => '$a',
                                type => 'Str',
                        ),
                        Parameter.new(
                                name => '$b',
                                type => 'Int',
                        ),
                        Parameter.new(
                                name => '*%_',
                        ),
                ),
                count => 3.Num,
        ),
        'Arity should max out at count, but not a big problem',
];
@signature-tests.push: [
        5,
        'True',
        Signature.new(
                params => (
                        Parameter.new(
                                type => 'A',
                                invocant => True,
                        ),
                        Parameter.new(
                                name => '$a',
                                type => 'Str',
                        ),
                        Parameter.new(
                                name => '$b',
                                type => 'Int',
                        ),
                        Parameter.new(
                                name => '*%_',
                        ),
                ),
                count => 3.Num,
                arity => 3,
        ),
        "Return value defaults to Mu, like the method, but since they're different Mus, this fails when it should pass",
];
@signature-tests.push: [
        6,
        'True',
        Signature.new(
                params => (
                        Parameter.new(
                                type => 'A',
                                invocant => True,
                        ),
                        Parameter.new(
                                name => '$a',
                                type => 'Str',
                        ),
                        Parameter.new(
                                name => '$b',
                                type => 'Int',
                        ),
                        Parameter.new(
                                name => '*%_',
                        ),
                ),
                count => 3.Num,
                arity => 3,
                returns => Nil,
        ),
        "Return value of Nil doesn't help either",
];

for @signature-tests -> $test {
        my ($count, $expected, $signature-test, $problem) = @$test;
        compare-signatures("signature-method and signature test $count", $expected, $signature-method, $signature-test, $problem);
}

Actual Behavior

signature-method and signature test 1
        Expectation: False
        sig1.ACCEPTS(sig2): False
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(Str $a, Int $b, *%_)
        arity: 3 vs. 2
        count: 3 vs. 2
        returns.WHICH: Mu|U4000030247816 vs. Mu|U4000030247816 = True
        returns.raku: Mu vs. Mu = True

signature-method and signature test 2
        Expectation: True
        sig1.ACCEPTS(sig2): True
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(A $:: Str $a, Int $b, *%_)
        arity: 3 vs. 3
        count: 3 vs. 3
        returns.WHICH: Mu|U4000030247816 vs. Mu|U4000030247816 = True
        returns.raku: Mu vs. Mu = True

signature-method and signature test 3
        Expectation: True
        sig1.ACCEPTS(sig2): False
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(A $:: Str $a, Int $b, *%_ --> Mu)
        arity: 3 vs. 4
        count: 3 vs. 4
        returns.WHICH: Mu|U4000030247816 vs. Mu|U4000030247816 = False
        returns.raku: Mu vs. Mu = False
        Problem: Arity and count should sort themselves out a little better, but not a big problem

signature-method and signature test 4
        Expectation: True
        sig1.ACCEPTS(sig2): False
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(A $:: Str $a, Int $b, *%_ --> Mu)
        arity: 3 vs. 4
        count: 3 vs. 3
        returns.WHICH: Mu|U4000030247816 vs. Mu|U4000030247816 = False
        returns.raku: Mu vs. Mu = False
        Problem: Arity should max out at count, but not a big problem

signature-method and signature test 5
        Expectation: True
        sig1.ACCEPTS(sig2): False
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(A $:: Str $a, Int $b, *%_ --> Mu)
        arity: 3 vs. 3
        count: 3 vs. 3
        returns.WHICH: Mu|U4000030247816 vs. Mu|U4000030247816 = False
        returns.raku: Mu vs. Mu = False
        Problem: Return value defaults to Mu, like the method, but since they're different Mus, this fails when it should pass

signature-method and signature test 6
        Expectation: True
        sig1.ACCEPTS(sig2): False
        Straight comparison
                sig1: :(A $:: Str $a, Int $b, *%_)
                sig2: :(A $:: Str $a, Int $b, *%_ --> Nil)
        arity: 3 vs. 3
        count: 3 vs. 3
        returns.WHICH: Mu|U4000030247816 vs. Nil|U4000030247840 = False
        returns.raku: Mu vs. Nil = False
        Problem: Return value of Nil doesn't help either

Expected Behavior

In the example given, test 5 should return true (as should 3 and 4, but they can be worked around).

Environment

  • Operating system: Alpine 3.20.0
  • Compiler version (rakudo -v or raku -v):

Welcome to Rakudo™ v2024.05. Implementing the Raku® Programming Language v6.d. Built on MoarVM version 2024.05.

wayland avatar Jun 04 '24 22:06 wayland

Just a note that the suggested fix may not fix the whole problem, but it should at least get us partway there.

wayland avatar Jun 04 '24 22:06 wayland