Skip to content

Commit 6017de0

Browse files
committed
Tune usage of resultConforms to prefer the chosen alternative
`resultConforms` should only make us change our mind when we're sure that the chosen alternative does not fit the result type but another alternative would. The problem is that due to approximations and adaptation, we can guess wrong on both counts. In i22713 in particular, we guessed that the chosen alternative does not work (but it does because of Unit insertion) and that another alternative would, but it doesn't because `resultConforms` prefers false positives to false negatives (the result type is under-approximated to `Nothing` which is indeed a subtype of `Unit`). We only really need to prefer false positives to false negatives when considering whether to discard the currently chosen alternative (because there's probably a good reason we chose it in the first place), so this commit introduces a parameter to `resultConforms` to control this. Fixes #22713.
1 parent 332fceb commit 6017de0

File tree

2 files changed

+19
-4
lines changed

2 files changed

+19
-4
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,7 +2125,7 @@ trait Applications extends Compatibility {
21252125
* Using an approximated result type is necessary to avoid false negatives
21262126
* due to incomplete type inference such as in tests/pos/i21410.scala and tests/pos/i21410b.scala.
21272127
*/
2128-
def resultConforms(altSym: Symbol, altType: Type, resultType: Type)(using Context): Boolean =
2128+
def resultConforms(altSym: Symbol, altType: Type, resultType: Type, avoidFalseNegatives: Boolean = false)(using Context): Boolean =
21292129
resultType.revealIgnored match {
21302130
case resultType: ValueType =>
21312131
altType.widen match {
@@ -2134,8 +2134,9 @@ trait Applications extends Compatibility {
21342134
val wildRes = wildApprox(tp.resultType)
21352135

21362136
class ResultApprox extends AvoidWildcardsMap:
2137-
// Avoid false negatives by approximating to a lower bound
2138-
variance = -1
2137+
if avoidFalseNegatives then
2138+
// Prefer false negatives to false positives by approximating to a lower bound
2139+
variance = -1
21392140

21402141
val approx = ResultApprox()(wildRes)
21412142
constrainResult(altSym, approx, resultType)
@@ -2157,7 +2158,7 @@ trait Applications extends Compatibility {
21572158
* do they prune much, on average.
21582159
*/
21592160
def adaptByResult(chosen: TermRef, alts: List[TermRef]) = pt match {
2160-
case pt: FunProto if !explore(resultConforms(chosen.symbol, chosen, pt.resultType)) =>
2161+
case pt: FunProto if !explore(resultConforms(chosen.symbol, chosen, pt.resultType, avoidFalseNegatives = true)) =>
21612162
val conformingAlts = alts.filterConserve(alt =>
21622163
(alt ne chosen) && explore(resultConforms(alt.symbol, alt, pt.resultType)))
21632164
conformingAlts match {

tests/pos/i22713.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait Invariant[T]
2+
3+
trait Foo
4+
trait Bar[T]
5+
6+
trait Worker:
7+
def schedule(command: Foo): Invariant[?]
8+
def schedule[V](callable: Bar[V]): Invariant[V]
9+
10+
object Test:
11+
def test(worker: Worker, foo: Foo): Option[Unit] =
12+
Some(worker.schedule(foo))
13+
// error: Found: Foo
14+
// Required: Bar[V]

0 commit comments

Comments
 (0)