Skip to content

Commit 6226567

Browse files
Prefer parameterless alternatives during ambiguous resolution, fix #10715
1 parent 8948b09 commit 6226567

File tree

6 files changed

+134
-21
lines changed

6 files changed

+134
-21
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,42 +3427,59 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
34273427
ErrorReporting.missingArgs(tree, mt)
34283428
tree.withType(mt.resultType)
34293429

3430-
def adaptOverloaded(ref: TermRef) = {
3430+
def adaptOverloaded(ref: TermRef) =
3431+
// get all the alternatives
34313432
val altDenots =
34323433
val allDenots = ref.denot.alternatives
34333434
if pt.isExtensionApplyProto then allDenots.filter(_.symbol.is(ExtensionMethod))
34343435
else allDenots
3436+
34353437
typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%\n\n %")
3438+
3439+
/** Search for an alternative that does not take parameters.
3440+
* If there is one, return it, otherwise emit an error.
3441+
*/
3442+
def tryParameterless(alts: List[TermRef])(error: => tpd.Tree): Tree =
3443+
alts.filter(_.info.isParameterless) match
3444+
case alt :: Nil => readaptSimplified(tree.withType(alt))
3445+
case _ =>
3446+
if altDenots.exists(_.info.paramInfoss == ListOfNil) then
3447+
typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt, locked)
3448+
else
3449+
error
3450+
34363451
def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt)
34373452
val alts = altDenots.map(altRef)
3438-
resolveOverloaded(alts, pt) match {
3453+
3454+
resolveOverloaded(alts, pt) match
34393455
case alt :: Nil =>
34403456
readaptSimplified(tree.withType(alt))
34413457
case Nil =>
3442-
// If alternative matches, there are still two ways to recover:
3458+
// If no alternative matches, there are still two ways to recover:
34433459
// 1. If context is an application, try to insert an apply or implicit
34443460
// 2. If context is not an application, pick a alternative that does
34453461
// not take parameters.
3446-
def noMatches =
3447-
errorTree(tree, NoMatchingOverload(altDenots, pt))
3448-
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil
3449-
pt match {
3462+
3463+
def errorNoMatch = errorTree(tree, NoMatchingOverload(altDenots, pt))
3464+
3465+
pt match
34503466
case pt: FunOrPolyProto if pt.applyKind != ApplyKind.Using =>
34513467
// insert apply or convert qualifier, but only for a regular application
3452-
tryInsertApplyOrImplicit(tree, pt, locked)(noMatches)
3468+
tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch)
34533469
case _ =>
3454-
alts.filter(_.info.isParameterless) match {
3455-
case alt :: Nil => readaptSimplified(tree.withType(alt))
3456-
case _ =>
3457-
if (altDenots exists (_.info.paramInfoss == ListOfNil))
3458-
typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt, locked)
3459-
else
3460-
noMatches
3461-
}
3462-
}
3470+
tryParameterless(alts)(errorNoMatch)
3471+
34633472
case ambiAlts =>
3464-
if tree.tpe.isErroneous || pt.isErroneous then tree.withType(UnspecifiedErrorType)
3465-
else
3473+
// If there are ambiguous alternatives, and:
3474+
// 1. the types aren't erroneous
3475+
// 2. the expected type is not a function type
3476+
// 3. there exist a parameterless alternative
3477+
//
3478+
// Then, pick the parameterless alternative.
3479+
// See tests/pos/i10715-scala and tests/pos/i10715-java.
3480+
3481+
/** Constructs an "ambiguous overload" error */
3482+
def errorAmbiguous =
34663483
val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot)))
34673484
val addendum =
34683485
if ambiAlts.exists(!_.symbol.exists) then
@@ -3471,8 +3488,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
34713488
|Note: Overloaded definitions introduced by refinements cannot be resolved"""
34723489
else ""
34733490
errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum))
3474-
}
3475-
}
3491+
end errorAmbiguous
3492+
3493+
if tree.tpe.isErroneous || pt.isErroneous then
3494+
tree.withType(UnspecifiedErrorType)
3495+
else
3496+
pt match
3497+
case _: FunProto =>
3498+
errorAmbiguous
3499+
case _ =>
3500+
tryParameterless(alts)(errorAmbiguous)
3501+
3502+
end match
3503+
end adaptOverloaded
34763504

34773505
def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match {
34783506
case wtp: MethodOrPoly =>

tests/neg/i10715a.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Parent:
2+
def f(x: Int): Parent = ???
3+
def f: Int = 0
4+
5+
def g[A](x: Int): Parent = ???
6+
def g[A]: Int = 0
7+
8+
class Sub extends Parent:
9+
override def f(x: Int): Parent = ???
10+
override def g[A](x: Int): Parent = ???
11+
12+
def bad(c: Sub): Unit =
13+
c.f: String // error
14+
c.g: String // error
15+
c.f.bad // error
16+
c.g.bad // error
17+
18+
c.f("") // error
19+
c.g("") // error
20+
c.g[Int]("") // error
21+
c.g[Int]: (String => String) // error
22+
c.g[Int]: (Int => Parent) // ok

tests/neg/i10715b.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Parent:
2+
def f(x: Int): Unit = ()
3+
def f: Int = 0
4+
5+
class Sub extends Parent:
6+
override def f(x: Int): Unit = ()
7+
def f(x: Int)(using String): Unit = ()
8+
9+
def bad(c: Sub): Unit =
10+
c.f(1) // error: ambiguous overload

tests/pos/i10715-java/C_1.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class C_1 {
2+
3+
public int f() {
4+
return 0;
5+
}
6+
public C_1 f(int x) {
7+
return null;
8+
}
9+
}
10+
11+
class Child extends C_1 {
12+
@Override
13+
public C_1 f(int x) {
14+
return null;
15+
}
16+
}

tests/pos/i10715-java/caller_2.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def test(c: Child): Unit =
2+
c.f() // always ok
3+
c.f // should work too
4+
c.f(1)
5+
c.f.toString
6+
7+
// The issue was first detected on NIO buffers,
8+
// (on Java 11+), so these should pass now.
9+
def buffer(c: java.nio.ByteBuffer): Unit =
10+
c.position
11+
c.position(10).position.toString

tests/pos/i10715-scala/test.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Parent:
2+
def f(x: Int): Parent = ???
3+
def f: Int = 0
4+
5+
def g[A](x: Int): Parent = ???
6+
def g[A]: Int = 0
7+
8+
// For the issue to show up, there must be a subclass that overrides
9+
// one of the two methods.
10+
class Sub extends Parent:
11+
override def f(x: Int): Parent = ???
12+
override def g[A](x: Int): Parent = ???
13+
14+
def test(c: Sub): Unit =
15+
c.f(1) // already worked
16+
c.f
17+
c.f.+(0)
18+
c.f.toString
19+
20+
c.g(0) // already worked
21+
c.g
22+
c.g[Int]
23+
c.g.+(0)
24+
c.g.toString
25+
c.g[Int].+(0)
26+
c.g.toString

0 commit comments

Comments
 (0)