Skip to content

Commit

Permalink
GROOVY-11258: parameter subtyping: select most-specific overload
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Sep 26, 2024
1 parent 7a7e7b2 commit 7576b86
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 15 deletions.
38 changes: 26 additions & 12 deletions src/main/java/groovy/lang/MetaClassImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3264,26 +3264,40 @@ private Object chooseMethodInternal(String methodName, Object methodOrList, Clas
return doChooseMostSpecificParams(theClass.getName(), methodName, matchingMethods, arguments, false);
}

protected static Object doChooseMostSpecificParams(String theClassName, String name, List matchingMethods, Class[] arguments, boolean checkParametersCompatible) {
long matchesDistance = -1;
LinkedList matches = new LinkedList();
protected static Object doChooseMostSpecificParams(final String theClassName, final String name, final List matchingMethods, final Class[] arguments, final boolean checkParameterCompatibility) {
var matchesDistance = -1L;
var matches = new LinkedList<>();
for (Object method : matchingMethods) {
final ParameterTypes parameterTypes = (ParameterTypes) method;
if (checkParametersCompatible && !MetaClassHelper.parametersAreCompatible(arguments, parameterTypes.getNativeParameterTypes()))
continue;
long dist = MetaClassHelper.calculateParameterDistance(arguments, parameterTypes);
if (dist == 0) return method;
matchesDistance = handleMatches(matchesDistance, matches, method, dist);
var parameterTypes = (ParameterTypes) method;
if (!checkParameterCompatibility || MetaClassHelper.parametersAreCompatible(arguments, parameterTypes.getNativeParameterTypes())) {
long dist = MetaClassHelper.calculateParameterDistance(arguments, parameterTypes);
matchesDistance = handleMatches(matchesDistance, matches, method, dist);
}
}

int size = matches.size();
if (size == 1) {
return matches.getFirst();
if (size > 1) { // GROOVY-11258
for (var iter = matches.iterator(); iter.hasNext(); ) {
Object outer = iter.next(); // if redundant, remove
for (Object inner : matches) {
if (inner == outer) continue;
Class<?>[] innerTypes = ((ParameterTypes) inner).getNativeParameterTypes();
Class<?>[] outerTypes = ((ParameterTypes) outer).getNativeParameterTypes();
if (!Arrays.equals(innerTypes, outerTypes) && MetaClassHelper.parametersAreCompatible(innerTypes, outerTypes)) {
iter.remove(); // for the given argument type(s), inner can accept everything that outer can
size -= 1;
break;
}
}
}
}

if (size == 0) {
return null;
}
// more than one matching method found --> ambiguous!
if (size == 1) {
return matches.getFirst();
}
throw new GroovyRuntimeException(createErrorMessageForAmbiguity(theClassName, name, arguments, matches));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,19 @@ private static List<MethodNode> chooseBestMethods(final ClassNode receiver, Coll
bestMethods.add(method);
}
}
if (bestMethods.size() > 1) { // GROOVY-11258
for (var iter = bestMethods.iterator(); iter.hasNext(); ) {
MethodNode outer = iter.next(); // if redundant, remove
for (MethodNode inner : bestMethods) {
if (inner != outer
&& !ParameterUtils.parametersEqual(inner.getParameters(), outer.getParameters())
&& ParameterUtils.parametersCompatible(inner.getParameters(), outer.getParameters())) {
iter.remove(); // for the given argument type(s), inner can accept everything that outer can
break;
}
}
}
}

// phase 2: receiver-provider distance classifier
if (bestMethods.size() > 1 && receiver != null) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/ListTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,13 @@ class ListTest extends GroovyTestCase {
assert list[-1..-2] == [3, 2] , 'negative index range reversed'
assert list[-1..<-2] == [3] , 'negative index range reversed right exclusive'
assert list[-1<..-2] == [2] , 'negative index range reversed left exclusive'
assert list[-1<..<-2] == [] , 'negative index range reversed full exclusive' // aaaaaaahhhhh !
assert list[-1<..<-2] == [] , 'negative index range reversed full exclusive'
assert list[0..-1] == list , 'pos - neg value'
assert list[0..<-1] == [0, 1, 2] , 'pos - neg value right exclusive'
assert list[0<..-1] == [1, 2, 3] , 'pos - neg value left exclusive'
assert list[0<..<-1] == [1, 2] , 'pos - neg value full exclusive'
assert list[0..<-2] == [0, 1] , 'pos - neg value exclusive'
shouldFail(GroovyRuntimeException) { list[null] }
shouldFail(NullPointerException) { list[null] }
shouldFail(IndexOutOfBoundsException) { list[5..6] }
}

Expand Down
19 changes: 18 additions & 1 deletion src/test/groovy/transform/stc/ClosuresSTCTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -867,8 +867,25 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
}
}

// GROOVY-11397
// GROOVY-11258
void testSAMsInMethodSelection7() {
assertScript '''import java.util.function.Function
// org.assertj.core.api.iterable.ThrowingExtractor
interface F<In,Out> extends Function<In,Out> {
default Out apply(In it) { redir(it) }
Out redir(In it) throws Exception
}
class C {
Number m(Function<Number, Number> f) { f.apply(1) }
Number m(F <Number, Number> f) { f.redir(2) }
}
Number which = new C().m { it }
assert which == 2
'''
}

// GROOVY-11397
void testSAMsInMethodSelection8() {
assertScript '''
interface Action<T> { void run(T t) }
interface Proc { void doSomething() }
Expand Down

0 comments on commit 7576b86

Please # to comment.