From c6a278a9480f16582c07f40f26e372cd9a362cec Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 9 Dec 2025 06:33:24 -0800 Subject: [PATCH 1/2] Vertical alignment in adaptOverloaded --- .../src/dotty/tools/dotc/typer/Typer.scala | 89 +++++++++---------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 210104c4ab93..c7a48e7c6c0e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4370,54 +4370,47 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val alts = altDenots.map(altRef) resolveOverloaded(alts, pt) match - case alt :: Nil => - readaptSimplified(tree.withType(alt)) - case Nil => - // If no alternative matches, there are still two ways to recover: - // 1. If context is an application, try to insert an apply or implicit - // 2. If context is not an application, pick a alternative that does - // not take parameters. - - def errorNoMatch = errorTree(tree, NoMatchingOverload(altDenots, pt)) - - pt match - case pt: FunOrPolyProto if pt.applyKind != ApplyKind.Using => - // insert apply or convert qualifier, but only for a regular application - tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch) - case _ => - tryParameterless(alts)(errorNoMatch) - - case ambiAlts => - // If there are ambiguous alternatives, and: - // 1. the types aren't erroneous - // 2. the expected type is not a function type - // 3. there exist a parameterless alternative - // - // Then, pick the parameterless alternative. - // See tests/pos/i10715-scala and tests/pos/i10715-java. - - /** Constructs an "ambiguous overload" error */ - def errorAmbiguous = - val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot))) - val addendum = - if ambiAlts.exists(!_.symbol.exists) then - i"""| - | - |Note: Overloaded definitions introduced by refinements cannot be resolved""" - else "" - errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum)) - end errorAmbiguous - - if tree.tpe.isErroneous || pt.isErroneous then - tree.withType(UnspecifiedErrorType) - else - pt match - case _: FunProto => - errorAmbiguous - case _ => - tryParameterless(alts)(errorAmbiguous) - - end match + case alt :: Nil => + readaptSimplified(tree.withType(alt)) + case Nil => + // If no alternative matches, there are still two ways to recover: + // 1. If context is an application, try to insert an apply or implicit + // 2. If context is not an application, pick an alternative that does + // not take parameters. + def errorNoMatch = errorTree(tree, NoMatchingOverload(altDenots, pt)) + + pt match + case pt: FunOrPolyProto if pt.applyKind != ApplyKind.Using => + // insert apply or convert qualifier, but only for a regular application + tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch) + case _ => + tryParameterless(alts)(errorNoMatch) + case ambiAlts => + // If there are ambiguous alternatives, and: + // 1. the types aren't erroneous + // 2. the expected type is not a function type + // 3. there exists a parameterless alternative + // + // Then, pick the parameterless alternative. See tests/pos/i10715-* + + /** Constructs an "ambiguous overload" error */ + def errorAmbiguous = + val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot))) + val addendum = + if ambiAlts.exists(!_.symbol.exists) then + i"""| + | + |Note: Overloaded definitions introduced by refinements cannot be resolved""" + else "" + errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum)) + + pt match + case pt if tree.tpe.isErroneous || pt.isErroneous => + tree.withType(UnspecifiedErrorType) + case _: FunProto => + errorAmbiguous + case _ => + tryParameterless(alts)(errorAmbiguous) end adaptOverloaded def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { From 3f07ef91af4d32ef21af8ae3379f4a991c42d840 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 9 Dec 2025 09:00:52 -0800 Subject: [PATCH 2/2] Construct ref more correctly --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 13 ++++++------- tests/pos/i24631/TestJava.java | 12 ++++++++++++ tests/pos/i24631/test.scala | 2 ++ tests/pos/i24631b.scala | 11 +++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i24631/TestJava.java create mode 100644 tests/pos/i24631/test.scala create mode 100644 tests/pos/i24631b.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c7a48e7c6c0e..24f69137d3cc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4352,23 +4352,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val allDenots = ref.denot.alternatives if pt.isExtensionApplyProto then allDenots.filter(_.symbol.is(ExtensionMethod)) else allDenots + def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt) + val alts = altDenots.map(altRef) typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%\n\n %") /** Search for an alternative that does not take parameters. * If there is one, return it, otherwise return the error tree. */ - def tryParameterless(alts: List[TermRef])(error: => tpd.Tree): Tree = + def tryParameterless(error: => tpd.Tree): Tree = alts.filter(_.info.isParameterless) match case alt :: Nil => readaptSimplified(tree.withType(alt)) case _ => altDenots.find(_.info.paramInfoss == ListOfNil) match - case Some(alt) => readaptSimplified(tree.withType(alt.symbol.denot.termRef)) + case Some(alt) => readaptSimplified(tree.withType(altRef(alt))) case _ => error - def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt) - val alts = altDenots.map(altRef) - resolveOverloaded(alts, pt) match case alt :: Nil => readaptSimplified(tree.withType(alt)) @@ -4384,7 +4383,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // insert apply or convert qualifier, but only for a regular application tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch) case _ => - tryParameterless(alts)(errorNoMatch) + tryParameterless(errorNoMatch) case ambiAlts => // If there are ambiguous alternatives, and: // 1. the types aren't erroneous @@ -4410,7 +4409,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _: FunProto => errorAmbiguous case _ => - tryParameterless(alts)(errorAmbiguous) + tryParameterless(errorAmbiguous) end adaptOverloaded def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { diff --git a/tests/pos/i24631/TestJava.java b/tests/pos/i24631/TestJava.java new file mode 100644 index 000000000000..1a869cbdd6db --- /dev/null +++ b/tests/pos/i24631/TestJava.java @@ -0,0 +1,12 @@ +package example; + +public abstract class TestJava { + public abstract T create(String foo); + + // Note that this is the method that's called from Scala code + public T create() { return create(""); } + + public static class Concrete extends TestJava { + @Override public String create(String foo) { return foo; } + } +} diff --git a/tests/pos/i24631/test.scala b/tests/pos/i24631/test.scala new file mode 100644 index 000000000000..c54eb7f98c69 --- /dev/null +++ b/tests/pos/i24631/test.scala @@ -0,0 +1,2 @@ +val s = new example.TestJava.Concrete().create +val s2: String = s diff --git a/tests/pos/i24631b.scala b/tests/pos/i24631b.scala new file mode 100644 index 000000000000..9e408729c6c3 --- /dev/null +++ b/tests/pos/i24631b.scala @@ -0,0 +1,11 @@ +//> using options -source:3.0-migration + +abstract class C[A]: + def create(s: String): A + def create(): A = create("") + +class D extends C[String]: + def create(s: String): String = s + +val s = D().create +val s2: String = s