From a71b76c594c9724d9fa0f9ab2231b1ba3d261f67 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 17 Oct 2025 02:36:04 -0700 Subject: [PATCH] Check extension method at typer --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 13 +++++++------ tests/warn/i16743.scala | 8 ++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 0605228d3a6e..1006044e851d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1136,8 +1136,8 @@ object RefChecks { * This check is suppressed if the method is an override. (Because the type of the receiver * may be narrower in the override.) * - * If the extension method is nilary, it is always hidden by a member of the same name. - * (Either the member is nilary, or the reference is taken as the eta-expansion of the member.) + * If the extension method is parameterless, it is always hidden by a member of the same name. + * (Either the member is parameterless, or the reference is taken as the eta-expansion of the member.) * * This check is in lieu of a more expensive use-site check that an application failed to use an extension. * That check would account for accessibility and opacity. As a limitation, this check considers @@ -1157,15 +1157,15 @@ object RefChecks { * parameters of the extension method must be distinguishable from the member parameters, as described above. */ def checkExtensionMethods(sym: Symbol)(using Context): Unit = - if sym.is(Extension) then + if sym.is(Extension) then atPhase(typerPhase): extension (tp: Type) def explicit = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true) def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false } - def isNilary = tp.stripPoly match { case mt: MethodType => false case _ => true } + def isParamLess = tp.stripPoly match { case mt: MethodType => false case _ => true } val explicitInfo = sym.info.explicit // consider explicit value params def memberHidesMethod(member: Denotation): Boolean = val methTp = explicitInfo.resultType // skip leading implicits and the "receiver" parameter - if methTp.isNilary then + if methTp.isParamLess then return true // extension without parens is always hidden by a member of same name val memberIsImplicit = member.info.hasImplicitParams inline def paramsCorrespond = @@ -1174,7 +1174,8 @@ object RefChecks { else methTp.explicit.firstParamTypes val memberParamTps = member.info.stripPoly.firstParamTypes memberParamTps.corresponds(paramTps): (m, x) => - m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias && (x frozen_<:< m) + m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias + && (x frozen_<:< m) memberIsImplicit && !methTp.hasImplicitParams || paramsCorrespond def targetOfHiddenExtension: Symbol = val target = diff --git a/tests/warn/i16743.scala b/tests/warn/i16743.scala index cca0ecbce90e..c347ed64d284 100644 --- a/tests/warn/i16743.scala +++ b/tests/warn/i16743.scala @@ -93,6 +93,11 @@ object Depending: def f(using d: Depends) = d.thing.y def g(using d: Depends) = d.thing.length() +class i24198: + def m(ss: String*) = ss.foldLeft("")(_ + _) +object i24198: + extension (r: i24198) def m(ss: Seq[String]) = ss.foldRight("!")(_ + _) + @main def test() = val x = new T {} println(x.f(42)) // OK! @@ -119,3 +124,6 @@ object Depending: given String = "42" x.w("27") } + println: + val sut = i24198() + sut.m(List("hello", ", ", "world"))