Skip to content

Commit d2eeef1

Browse files
committed
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
1 parent bf03086 commit d2eeef1

File tree

3 files changed

+66
-12
lines changed

3 files changed

+66
-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/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)