Raku Signature not able to generate a method signature
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 -vorraku -v):
Welcome to Rakudo™ v2024.05. Implementing the Raku® Programming Language v6.d. Built on MoarVM version 2024.05.
Just a note that the suggested fix may not fix the whole problem, but it should at least get us partway there.