Skip to content

Commit 2de3cf4

Browse files
committed
Fix #5202: Refine argForParam
argForParam was overly naive for the cases where the prefix was an & or | type.
1 parent bb6a6cb commit 2de3cf4

File tree

2 files changed

+36
-16
lines changed

2 files changed

+36
-16
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,9 +1988,10 @@ object Types {
19881988
}
19891989

19901990
/** The argument corresponding to class type parameter `tparam` as seen from
1991-
* prefix `pre`.
1991+
* prefix `pre`. Can produce a TypeBounds type in case prefix is an & or | type
1992+
* and parameter is non-variant.
19921993
*/
1993-
def argForParam(pre: Type)(implicit ctx: Context): Type = {
1994+
def argForParam(pre: Type, variance: Int)(implicit ctx: Context): Type = {
19941995
val tparam = symbol
19951996
val cls = tparam.owner
19961997
val base = pre.baseType(cls)
@@ -2010,10 +2011,17 @@ object Types {
20102011
idx += 1
20112012
}
20122013
NoType
2013-
case OrType(base1, base2) => argForParam(base1) | argForParam(base2)
2014-
case AndType(base1, base2) => argForParam(base1) & argForParam(base2)
2014+
case base: AndOrType =>
2015+
var tp1 = argForParam(base.tp1, variance)
2016+
var tp2 = argForParam(base.tp2, variance)
2017+
if (tp1.isInstanceOf[TypeBounds] || tp2.isInstanceOf[TypeBounds] || variance == 0) {
2018+
// compute argument as a type bounds instead of a point type
2019+
tp1 = tp1.bounds
2020+
tp2 = tp2.bounds
2021+
}
2022+
if (base.isAnd == variance >= 0) tp1 & tp2 else tp1 | tp2
20152023
case _ =>
2016-
if (pre.termSymbol is Package) argForParam(pre.select(nme.PACKAGE))
2024+
if (pre.termSymbol is Package) argForParam(pre.select(nme.PACKAGE), variance)
20172025
else if (pre.isBottomType) pre
20182026
else NoType
20192027
}
@@ -2037,7 +2045,7 @@ object Types {
20372045
else {
20382046
if (isType) {
20392047
val res =
2040-
if (currentSymbol.is(ClassTypeParam)) argForParam(prefix)
2048+
if (currentSymbol.is(ClassTypeParam)) argForParam(prefix, currentSymbol.paramVariance)
20412049
else prefix.lookupRefined(name)
20422050
if (res.exists) return res
20432051
if (Config.splitProjections)
@@ -4462,14 +4470,17 @@ object Types {
44624470
* If the expansion is a wildcard parameter reference, convert its
44634471
* underlying bounds to a range, otherwise return the expansion.
44644472
*/
4465-
def expandParam(tp: NamedType, pre: Type): Type = tp.argForParam(pre) match {
4466-
case arg @ TypeRef(pre, _) if pre.isArgPrefixOf(arg.symbol) =>
4467-
arg.info match {
4468-
case TypeBounds(lo, hi) => range(atVariance(-variance)(reapply(lo)), reapply(hi))
4469-
case arg => reapply(arg)
4470-
}
4471-
case arg => reapply(arg)
4472-
}
4473+
def expandParam(tp: NamedType, pre: Type, variance: Int): Type =
4474+
tp.argForParam(pre, variance) match {
4475+
case arg @ TypeRef(pre, _) if pre.isArgPrefixOf(arg.symbol) =>
4476+
arg.info match {
4477+
case TypeBounds(lo, hi) => range(atVariance(-variance)(reapply(lo)), reapply(hi))
4478+
case arg => reapply(arg)
4479+
}
4480+
case TypeBounds(lo, hi) =>
4481+
range(lo, hi)
4482+
case arg => reapply(arg)
4483+
}
44734484

44744485
/** Derived selection.
44754486
* @pre the (upper bound of) prefix `pre` has a member named `tp.name`.
@@ -4479,12 +4490,15 @@ object Types {
44794490
else pre match {
44804491
case Range(preLo, preHi) =>
44814492
val forwarded =
4482-
if (tp.symbol.is(ClassTypeParam)) expandParam(tp, preHi)
4493+
if (tp.symbol.is(ClassTypeParam)) expandParam(tp, preHi, tp.symbol.paramVariance)
44834494
else tryWiden(tp, preHi)
44844495
forwarded.orElse(
44854496
range(super.derivedSelect(tp, preLo), super.derivedSelect(tp, preHi)))
44864497
case _ =>
4487-
super.derivedSelect(tp, pre)
4498+
super.derivedSelect(tp, pre) match {
4499+
case TypeBounds(lo, hi) => range(lo, hi)
4500+
case tp => tp
4501+
}
44884502
}
44894503

44904504
override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type =

tests/neg/i5202.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
val f: (Int => Int) | (String => Int) = (a: Int) => a + 3
3+
4+
f.apply(5) // error - found: Int expected: Int & String
5+
f("c") // error - found: String expected: Int & String
6+
}

0 commit comments

Comments
 (0)