From d2eeef1f9a84ebbd36f53740aa997dd77d438281 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 4 Sep 2022 13:23:45 +0200 Subject: [PATCH 1/2] Refine estimation of default arguments in overloading resolution When doing a "size-fits" check, we previously only worked with the fact whether the given alternative had default parameters or not. We know count the number of default parameters in the applied parameter section, which gives us a better estimate. Fixes #15898 --- .../dotty/tools/dotc/typer/Applications.scala | 34 ++++++++++++------- tests/neg/i15898.scala | 22 ++++++++++++ tests/pos/i15898.scala | 22 ++++++++++++ 3 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 tests/neg/i15898.scala create mode 100644 tests/pos/i15898.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index b7cabbebee71..e11afe261458 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1935,11 +1935,19 @@ trait Applications extends Compatibility { val ptypes = tp.paramInfos val numParams = ptypes.length def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam - def hasDefault = alt.symbol.hasDefaultParams - if (numParams == numArgs) true - else if (numParams < numArgs) isVarArgs - else if (numParams > numArgs + 1) hasDefault - else isVarArgs || hasDefault + def numDefaultParams = + if alt.symbol.hasDefaultParams then + trimParamss(tp, alt.symbol.rawParamss) match + case params :: _ => params.count(_.is(HasDefault)) + case _ => 0 + else 0 + if numParams < numArgs then isVarArgs + else if numParams == numArgs then true + else + val numNecessaryArgs = numParams - numDefaultParams + if numNecessaryArgs <= numArgs then true + else if numNecessaryArgs == numArgs + 1 then isVarArgs + else false case _ => numArgs == 0 } @@ -2080,6 +2088,14 @@ trait Applications extends Compatibility { } end resolveOverloaded1 + /** The largest suffix of `paramss` that has the same first parameter name as `t` */ + def trimParamss(t: Type, paramss: List[List[Symbol]])(using Context): List[List[Symbol]] = t match + case MethodType(Nil) => trimParamss(t.resultType, paramss) + case t: MethodOrPoly => + val firstParamName = t.paramNames.head + paramss.dropWhile(_.head.name != firstParamName) + case _ => Nil + /** Resolve overloading by mapping to a different problem where each alternative's * type is mapped with `f`, alternatives with non-existing types are dropped, and the * expected type is `pt`. Map the results back to the original alternatives. @@ -2089,13 +2105,7 @@ trait Applications extends Compatibility { val t = f(alt) if t.exists then val mappedSym = alt.symbol.asTerm.copy(info = t) - mappedSym.rawParamss = alt.symbol.rawParamss - // we need rawParamss to find parameters with default arguments, - // but we do not need to be precise right now, since this is just a pre-test before - // we look up default getters. If at some point we extract default arguments from the - // parameter symbols themselves, we have to find the right parameter by name, not position. - // That means it's OK to copy parameters wholesale rather than tailoring them to always - // correspond to the type transformation. + mappedSym.rawParamss = trimParamss(t, alt.symbol.rawParamss) Some((TermRef(NoPrefix, mappedSym), alt)) else None diff --git a/tests/neg/i15898.scala b/tests/neg/i15898.scala new file mode 100644 index 000000000000..374fc49600eb --- /dev/null +++ b/tests/neg/i15898.scala @@ -0,0 +1,22 @@ +object O { + class AC(code: => Unit) { + def apply() = code + + def this(code: => Unit, key: Int = 1, modifiers: Int = 0) = { + this(code) + } + } + + class Doc { + def method: Boolean = true + } + + val doc = new Doc + + val ac = new AC(doc.method) // error + + def foo[T](code: => Unit): Unit = () + def foo[T](code: => Unit, key: Int = 1, modifiers: Int = 0): Unit = foo(code) + foo(doc.method) // error + foo[Int](doc.method) // error +} \ No newline at end of file diff --git a/tests/pos/i15898.scala b/tests/pos/i15898.scala new file mode 100644 index 000000000000..31725bf1667a --- /dev/null +++ b/tests/pos/i15898.scala @@ -0,0 +1,22 @@ +object O { + class AC(code: => Unit) { + def apply() = code + + def this(code: => Unit, key: Int, modifiers: Int = 0) = { + this(code) + } + } + + class Doc { + def method: Boolean = true + } + + val doc = new Doc + + val ac = new AC(doc.method) + + def foo[T](code: => Unit): Unit = () + def foo[T](code: => Unit, key: Int, modifiers: Int = 0): Unit = foo(code) + foo(doc.method) + foo[Int](doc.method) +} \ No newline at end of file From 85b5e02f748b5332020ed45c3098d2672dcc612e Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 4 Sep 2022 13:27:28 +0200 Subject: [PATCH 2/2] Add pending tests Some tests for issues that have not been fixed --- tests/pending/pos/i15915.scala | 24 +++++++++++++ tests/pending/pos/i15926.scala | 31 ++++++++++++++++ tests/pending/run/i15893.scala | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 tests/pending/pos/i15915.scala create mode 100644 tests/pending/pos/i15926.scala create mode 100644 tests/pending/run/i15893.scala diff --git a/tests/pending/pos/i15915.scala b/tests/pending/pos/i15915.scala new file mode 100644 index 000000000000..7c484b242cc7 --- /dev/null +++ b/tests/pending/pos/i15915.scala @@ -0,0 +1,24 @@ +class _Monoid[A] +object _Monoid { + implicit val Monoid: _Monoid[Int] = new _Monoid[Int] +} + +class Lifecycle[A] +object Lifecycle { + + implicit def monoidForLifecycle[Monoid[_], A]( + implicit + monoidType: GetMonoidType[Monoid], + monoidA: Monoid[A] + ): Monoid[Lifecycle[A]] = new _Monoid().asInstanceOf[Monoid[Lifecycle[A]]] + +} + +sealed class GetMonoidType[C[_]] +object GetMonoidType { + implicit val getMonoid: GetMonoidType[_Monoid] = new GetMonoidType[_Monoid] +} + +object App extends App { + println(implicitly[_Monoid[Lifecycle[Int]]]) +} \ No newline at end of file diff --git a/tests/pending/pos/i15926.scala b/tests/pending/pos/i15926.scala new file mode 100644 index 000000000000..44faf17ffd54 --- /dev/null +++ b/tests/pending/pos/i15926.scala @@ -0,0 +1,31 @@ +//@main def main(): Unit = +// println(summon[Sum[Minus[Succ[Zero]], Minus[Succ[Zero]]] =:= Minus[Succ[Succ[Zero]]]]) + +sealed trait IntT +sealed trait NatT extends IntT +final case class Zero() extends NatT +final case class Succ[+N <: NatT](n: N) extends NatT +final case class Minus[+N <: Succ[NatT]](n: N) extends IntT + +type NatSum[X <: NatT, Y <: NatT] <: NatT = Y match + case Zero => X + case Succ[y] => NatSum[Succ[X], y] + +type NatDif[X <: NatT, Y <: NatT] <: IntT = Y match + case Zero => X + case Succ[y] => X match + case Zero => Minus[Y] + case Succ[x] => NatDif[x, y] + +type Sum[X <: IntT, Y <: IntT] <: IntT = Y match + case Zero => X + case Minus[y] => X match + case Minus[x] => Minus[NatSum[x, y]] + case _ => NatDif[X, y] + case _ => X match + case Minus[x] => NatDif[Y, x] + case _ => NatSum[X, Y] + +def test = + val x: Sum[Minus[Succ[Zero]], Minus[Succ[Zero]]] = ??? + val y = x diff --git a/tests/pending/run/i15893.scala b/tests/pending/run/i15893.scala new file mode 100644 index 000000000000..dedec2138f2a --- /dev/null +++ b/tests/pending/run/i15893.scala @@ -0,0 +1,65 @@ +sealed trait NatT +case class Zero() extends NatT +case class Succ[+N <: NatT](n: N) extends NatT + +type Mod2[N <: NatT] <: NatT = N match + case Zero => Zero + case Succ[Zero] => Succ[Zero] + case Succ[Succ[predPredN]] => Mod2[predPredN] + + +def mod2(n: NatT): NatT = n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => mod2(predPredN) + +/* +inline def inlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => inlineMod2(predPredN) + +transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) +*/ +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected + case Zero(): Zero => Zero() + case Succ(Zero()): Succ[Zero] => Succ(Zero()) + case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) +/* +inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() + case Succ(Zero()): Succ[Zero] => Succ(Zero()) + case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN) + +transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() + case Succ(Zero()): Succ[Zero] => Succ(Zero()) + case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN) + +def foo(n: NatT): NatT = mod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n +*/ +@main def main(): Unit = +/* + println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected + println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected +*/ + println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected +// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected +// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected