Skip to content

Commit b5fea82

Browse files
Refine estimation of default arguments in overloading resolution (#15962)
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
2 parents fede6fd + 85b5e02 commit b5fea82

File tree

6 files changed

+186
-12
lines changed

6 files changed

+186
-12
lines changed

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

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,11 +1935,19 @@ trait Applications extends Compatibility {
19351935
val ptypes = tp.paramInfos
19361936
val numParams = ptypes.length
19371937
def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam
1938-
def hasDefault = alt.symbol.hasDefaultParams
1939-
if (numParams == numArgs) true
1940-
else if (numParams < numArgs) isVarArgs
1941-
else if (numParams > numArgs + 1) hasDefault
1942-
else isVarArgs || hasDefault
1938+
def numDefaultParams =
1939+
if alt.symbol.hasDefaultParams then
1940+
trimParamss(tp, alt.symbol.rawParamss) match
1941+
case params :: _ => params.count(_.is(HasDefault))
1942+
case _ => 0
1943+
else 0
1944+
if numParams < numArgs then isVarArgs
1945+
else if numParams == numArgs then true
1946+
else
1947+
val numNecessaryArgs = numParams - numDefaultParams
1948+
if numNecessaryArgs <= numArgs then true
1949+
else if numNecessaryArgs == numArgs + 1 then isVarArgs
1950+
else false
19431951
case _ =>
19441952
numArgs == 0
19451953
}
@@ -2080,6 +2088,14 @@ trait Applications extends Compatibility {
20802088
}
20812089
end resolveOverloaded1
20822090

2091+
/** The largest suffix of `paramss` that has the same first parameter name as `t` */
2092+
def trimParamss(t: Type, paramss: List[List[Symbol]])(using Context): List[List[Symbol]] = t match
2093+
case MethodType(Nil) => trimParamss(t.resultType, paramss)
2094+
case t: MethodOrPoly =>
2095+
val firstParamName = t.paramNames.head
2096+
paramss.dropWhile(_.head.name != firstParamName)
2097+
case _ => Nil
2098+
20832099
/** Resolve overloading by mapping to a different problem where each alternative's
20842100
* type is mapped with `f`, alternatives with non-existing types are dropped, and the
20852101
* expected type is `pt`. Map the results back to the original alternatives.
@@ -2089,13 +2105,7 @@ trait Applications extends Compatibility {
20892105
val t = f(alt)
20902106
if t.exists then
20912107
val mappedSym = alt.symbol.asTerm.copy(info = t)
2092-
mappedSym.rawParamss = alt.symbol.rawParamss
2093-
// we need rawParamss to find parameters with default arguments,
2094-
// but we do not need to be precise right now, since this is just a pre-test before
2095-
// we look up default getters. If at some point we extract default arguments from the
2096-
// parameter symbols themselves, we have to find the right parameter by name, not position.
2097-
// That means it's OK to copy parameters wholesale rather than tailoring them to always
2098-
// correspond to the type transformation.
2108+
mappedSym.rawParamss = trimParamss(t, alt.symbol.rawParamss)
20992109
Some((TermRef(NoPrefix, mappedSym), alt))
21002110
else
21012111
None

tests/neg/i15898.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object O {
2+
class AC(code: => Unit) {
3+
def apply() = code
4+
5+
def this(code: => Unit, key: Int = 1, modifiers: Int = 0) = {
6+
this(code)
7+
}
8+
}
9+
10+
class Doc {
11+
def method: Boolean = true
12+
}
13+
14+
val doc = new Doc
15+
16+
val ac = new AC(doc.method) // error
17+
18+
def foo[T](code: => Unit): Unit = ()
19+
def foo[T](code: => Unit, key: Int = 1, modifiers: Int = 0): Unit = foo(code)
20+
foo(doc.method) // error
21+
foo[Int](doc.method) // error
22+
}

tests/pending/pos/i15915.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class _Monoid[A]
2+
object _Monoid {
3+
implicit val Monoid: _Monoid[Int] = new _Monoid[Int]
4+
}
5+
6+
class Lifecycle[A]
7+
object Lifecycle {
8+
9+
implicit def monoidForLifecycle[Monoid[_], A](
10+
implicit
11+
monoidType: GetMonoidType[Monoid],
12+
monoidA: Monoid[A]
13+
): Monoid[Lifecycle[A]] = new _Monoid().asInstanceOf[Monoid[Lifecycle[A]]]
14+
15+
}
16+
17+
sealed class GetMonoidType[C[_]]
18+
object GetMonoidType {
19+
implicit val getMonoid: GetMonoidType[_Monoid] = new GetMonoidType[_Monoid]
20+
}
21+
22+
object App extends App {
23+
println(implicitly[_Monoid[Lifecycle[Int]]])
24+
}

tests/pending/pos/i15926.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@main def main(): Unit =
2+
// println(summon[Sum[Minus[Succ[Zero]], Minus[Succ[Zero]]] =:= Minus[Succ[Succ[Zero]]]])
3+
4+
sealed trait IntT
5+
sealed trait NatT extends IntT
6+
final case class Zero() extends NatT
7+
final case class Succ[+N <: NatT](n: N) extends NatT
8+
final case class Minus[+N <: Succ[NatT]](n: N) extends IntT
9+
10+
type NatSum[X <: NatT, Y <: NatT] <: NatT = Y match
11+
case Zero => X
12+
case Succ[y] => NatSum[Succ[X], y]
13+
14+
type NatDif[X <: NatT, Y <: NatT] <: IntT = Y match
15+
case Zero => X
16+
case Succ[y] => X match
17+
case Zero => Minus[Y]
18+
case Succ[x] => NatDif[x, y]
19+
20+
type Sum[X <: IntT, Y <: IntT] <: IntT = Y match
21+
case Zero => X
22+
case Minus[y] => X match
23+
case Minus[x] => Minus[NatSum[x, y]]
24+
case _ => NatDif[X, y]
25+
case _ => X match
26+
case Minus[x] => NatDif[Y, x]
27+
case _ => NatSum[X, Y]
28+
29+
def test =
30+
val x: Sum[Minus[Succ[Zero]], Minus[Succ[Zero]]] = ???
31+
val y = x

tests/pending/run/i15893.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
sealed trait NatT
2+
case class Zero() extends NatT
3+
case class Succ[+N <: NatT](n: N) extends NatT
4+
5+
type Mod2[N <: NatT] <: NatT = N match
6+
case Zero => Zero
7+
case Succ[Zero] => Succ[Zero]
8+
case Succ[Succ[predPredN]] => Mod2[predPredN]
9+
10+
11+
def mod2(n: NatT): NatT = n match
12+
case Zero() => Zero()
13+
case Succ(Zero()) => Succ(Zero())
14+
case Succ(Succ(predPredN)) => mod2(predPredN)
15+
16+
/*
17+
inline def inlineMod2(inline n: NatT): NatT = inline n match
18+
case Zero() => Zero()
19+
case Succ(Zero()) => Succ(Zero())
20+
case Succ(Succ(predPredN)) => inlineMod2(predPredN)
21+
22+
transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match
23+
case Zero() => Zero()
24+
case Succ(Zero()) => Succ(Zero())
25+
case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN)
26+
*/
27+
def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected
28+
case Zero(): Zero => Zero()
29+
case Succ(Zero()): Succ[Zero] => Succ(Zero())
30+
case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN)
31+
/*
32+
inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
33+
case Zero(): Zero => Zero()
34+
case Succ(Zero()): Succ[Zero] => Succ(Zero())
35+
case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN)
36+
37+
transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
38+
case Zero(): Zero => Zero()
39+
case Succ(Zero()): Succ[Zero] => Succ(Zero())
40+
case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN)
41+
42+
def foo(n: NatT): NatT = mod2(n) match
43+
case Succ(Zero()) => Zero()
44+
case _ => n
45+
46+
inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match
47+
case Succ(Zero()) => Zero()
48+
case _ => n
49+
50+
inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match
51+
case Succ(Zero()) => Zero()
52+
case _ => n
53+
*/
54+
@main def main(): Unit =
55+
/*
56+
println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
57+
println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
58+
println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
59+
println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected
60+
println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
61+
println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
62+
*/
63+
println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected
64+
// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected
65+
// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected

tests/pos/i15898.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object O {
2+
class AC(code: => Unit) {
3+
def apply() = code
4+
5+
def this(code: => Unit, key: Int, modifiers: Int = 0) = {
6+
this(code)
7+
}
8+
}
9+
10+
class Doc {
11+
def method: Boolean = true
12+
}
13+
14+
val doc = new Doc
15+
16+
val ac = new AC(doc.method)
17+
18+
def foo[T](code: => Unit): Unit = ()
19+
def foo[T](code: => Unit, key: Int, modifiers: Int = 0): Unit = foo(code)
20+
foo(doc.method)
21+
foo[Int](doc.method)
22+
}

0 commit comments

Comments
 (0)