diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 921e3ca86fe4..5ac9e43d0cdc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1518,13 +1518,20 @@ trait Applications extends Compatibility { isApplicableMethodRef(_, args, resultType, ArgMatch.Compatible) } - private def onMethod(tp: Type, followApply: Boolean)(p: TermRef => Boolean)(using Context): Boolean = tp match { + + private def onMethod(tp: Type, followApply: Boolean)(p: TermRef => Boolean)(using Context): Boolean = + def hasApply: Boolean = + followApply && tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) + + tp match { + case expr: TermRef if expr.widenSingleton.isInstanceOf[ExprType] => + hasApply case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] => p(methRef) case mt: MethodicType => p(mt.narrow) case _ => - followApply && tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) + hasApply } /** Does `tp` have an extension method named `xname` with this-argument `argType` and @@ -1661,7 +1668,7 @@ trait Applications extends Compatibility { tp2.isVarArgsMethod && isApplicableMethodRef(alt2, tp1.paramInfos.map(_.repeatedToSingle), WildcardType, ArgMatch.Compatible) else - isApplicableMethodRef(alt2, tp1.paramInfos, WildcardType, ArgMatch.Compatible) + isApplicableTerm(tp1.paramInfos, WildcardType, ArgMatch.Compatible)(alt2) } case tp1: PolyType => // (2) inContext(ctx.fresh.setExploreTyperState()) { @@ -1804,8 +1811,7 @@ trait Applications extends Compatibility { def narrowMostSpecific(alts: List[TermRef])(using Context): List[TermRef] = { record("narrowMostSpecific") alts match { - case Nil => alts - case _ :: Nil => alts + case Nil | _ :: Nil => alts case alt1 :: alt2 :: Nil => compare(alt1, alt2) match { case 1 => alt1 :: Nil @@ -1937,6 +1943,21 @@ trait Applications extends Compatibility { else resolve(alts) end resolveOverloaded + def isApplicableTerm(argTypes: List[Type], resultType: Type, argMatch: ArgMatch)(using Context): TermRef => Boolean = + term => term.widen match + case _: AppliedType => + isApplicableType(term, argTypes, resultType) + case _ => + isApplicableMethodRef(term, argTypes, resultType, argMatch) + + def isApplicableTerm(argTypes: List[Tree], resultType: Type, keepConstraint: Boolean, argMatch: ArgMatch)(using Context): TermRef => Boolean = + term => term.widen match + case _: AppliedType => + isApplicableType(term, argTypes, resultType, keepConstraint) + case _ => + isApplicableMethodRef(term, argTypes, resultType, keepConstraint, argMatch) + + /** This private version of `resolveOverloaded` does the bulk of the work of * overloading resolution, but does neither result adaptation nor apply insertion. * It might be called twice from the public `resolveOverloaded` method, once with @@ -1972,7 +1993,8 @@ trait Applications extends Compatibility { } def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = - alts.filterConserve(isApplicableMethodRef(_, argTypes, resultType, ArgMatch.CompatibleCAP)) + alts.filterConserve: + isApplicableTerm(argTypes, resultType, ArgMatch.CompatibleCAP) /** Normalization steps before checking arguments: * @@ -2047,9 +2069,8 @@ trait Applications extends Compatibility { alts def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = - alts.filterConserve(alt => - isApplicableMethodRef(alt, args, resultType, keepConstraint = false, ArgMatch.CompatibleCAP) - ) + alts.filterConserve: + isApplicableTerm(args, resultType, keepConstraint = false, ArgMatch.CompatibleCAP) record("resolveOverloaded.FunProto", alts.length) val alts1 = narrowBySize(alts) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4ddeb6f66b85..7e854d181280 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3684,8 +3684,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Search for an alternative that does not take parameters. * If there is one, return it, otherwise emit an error. */ - def tryParameterless(alts: List[TermRef])(error: => tpd.Tree): Tree = - alts.filter(_.info.isParameterless) match + def noApplyNoParams: TermRef => Boolean = + alt => alt.info.isParameterless && !alt.info.member(nme.apply).exists + + def tryNoApplyParameterless(alts: List[TermRef])(error: => tpd.Tree): Tree = + alts.filter(noApplyNoParams) match case alt :: Nil => readaptSimplified(tree.withType(alt)) case _ => if altDenots.exists(_.info.paramInfoss == ListOfNil) then @@ -3712,7 +3715,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) + tryNoApplyParameterless(alts)(errorNoMatch) case ambiAlts => // If there are ambiguous alternatives, and: @@ -3739,11 +3742,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree.withType(UnspecifiedErrorType) else pt match - case _: FunProto => - errorAmbiguous - case _ => - tryParameterless(alts)(errorAmbiguous) - + case _: FunProto => errorAmbiguous + case _ => tryNoApplyParameterless(alts)(errorAmbiguous) end match end adaptOverloaded diff --git a/tests/neg/i18294.check b/tests/neg/i18294.check new file mode 100644 index 000000000000..35fe730f8ae3 --- /dev/null +++ b/tests/neg/i18294.check @@ -0,0 +1,54 @@ +-- [E051] Reference Error: tests/neg/i18294.scala:18:30 ---------------------------------------------------------------- +18 | val test01: Int => String = f1 // error + | ^^ + | Ambiguous overload. The overloaded alternatives of method f1 with types + | => Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` +-- [E051] Reference Error: tests/neg/i18294.scala:19:30 ---------------------------------------------------------------- +19 | val test02: Int => String = f2 // error + | ^^ + | Ambiguous overload. The overloaded alternatives of value f2 with types + | Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` +-- [E051] Reference Error: tests/neg/i18294.scala:22:36 ---------------------------------------------------------------- +22 | val test1: Int => String = DefDef.f4 // error + | ^^^^^^^^^ + | Ambiguous overload. The overloaded alternatives of method f4 in object DefDef with types + | => Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` +-- [E051] Reference Error: tests/neg/i18294.scala:23:37 ---------------------------------------------------------------- +23 | def dtest1: Int => String = DefDef.f4 // error + | ^^^^^^^^^ + | Ambiguous overload. The overloaded alternatives of method f4 in object DefDef with types + | => Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` +-- [E051] Reference Error: tests/neg/i18294.scala:25:36 ---------------------------------------------------------------- +25 | val test2: Int => String = DefVal.f5 // error + | ^^^^^^^^^ + | Ambiguous overload. The overloaded alternatives of value f5 in object DefVal with types + | Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` +-- [E051] Reference Error: tests/neg/i18294.scala:26:37 ---------------------------------------------------------------- +26 | def dtest2: Int => String = DefVal.f5 // error + | ^^^^^^^^^ + | Ambiguous overload. The overloaded alternatives of value f5 in object DefVal with types + | Int => String + | (s: Int): String + | both match expected type Int => String + | + | longer explanation available when compiling with `-explain` \ No newline at end of file diff --git a/tests/neg/i18294.scala b/tests/neg/i18294.scala new file mode 100644 index 000000000000..8088e3772dbd --- /dev/null +++ b/tests/neg/i18294.scala @@ -0,0 +1,26 @@ +def f1(s: Int): String = "a" +def f1: Int => String = _ => "b" + +def f2(s: Int): String = "a" +val f2: Int => String = _ => "b" + +def f3(s: Int): String = "a" +def f3(): Int => String = _ => "b" + +object DefDef: + def f4(s: Int): String = "a" + def f4: Int => String = _ => "b" +object DefVal: + def f5(s: Int): String = "a" + val f5: Int => String = _ => "b" + +@main def Test = + val test01: Int => String = f1 // error + val test02: Int => String = f2 // error + val test03: Int => String = f3 + + val test1: Int => String = DefDef.f4 // error + def dtest1: Int => String = DefDef.f4 // error + + val test2: Int => String = DefVal.f5 // error + def dtest2: Int => String = DefVal.f5 // error