Skip to content

Commit da20634

Browse files
committed
Fix #5844: Sanitize base type before substituting wildcard arguments
1 parent 7ff971f commit da20634

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,58 @@ object SymDenotations {
17271727
baseTp
17281728
}
17291729

1730+
/** Sanitize base type so that wildvard arguments can be safely handled by
1731+
* substitution. If base type is of the form `B[S_1, ..., S_n]`, then
1732+
* for every `S_i:
1733+
* for every wildcard argument args_j in `args`:
1734+
* if the corresponding parameter `tparam_j` is referenced by `S_i`,
1735+
* and the reference does not itself appear in argument position, then
1736+
* replace S_i with `_`.
1737+
*
1738+
* This prevents a non-sensical substitution of `tparam_j` in `S_i` by a wildcard
1739+
* type (of class TypeBounds). See pos/i5844.scala as a test case.
1740+
*/
1741+
def handleWildcards(base: Type, tparams: List[Symbol], args: List[Type]): Type = {
1742+
def sanitize(baseArg: Type, tparams: List[Symbol], args: List[Type]): Type = args match {
1743+
case arg :: args1 =>
1744+
val tparam :: tparams1 = tparams
1745+
val problemRef = new TypeAccumulator[Boolean] {
1746+
// Does `tp` contain a reference to `tparam` that does not appear itself
1747+
// in argument position of an AppliedType with a class constructor?
1748+
def apply(x: Boolean, tp: Type): Boolean =
1749+
x || {
1750+
tp match {
1751+
case tp @ TypeRef(prefix, _) =>
1752+
tp.symbol == tparam || apply(x, prefix)
1753+
case AppliedType(tycon, args) if tycon.typeSymbol.isClass =>
1754+
apply(x, tycon) || args.exists(foldOver(x, _))
1755+
case _ =>
1756+
foldOver(x, tp)
1757+
}
1758+
}
1759+
}
1760+
def isParamRef(tp: Type) = tp match {
1761+
case tp: TypeRef => tp.symbol == tparam
1762+
case _ => false
1763+
}
1764+
val baseArg1 = arg match {
1765+
case arg: TypeBounds if !isParamRef(baseArg) && problemRef(false, baseArg) =>
1766+
TypeBounds.empty
1767+
case _ =>
1768+
baseArg
1769+
}
1770+
sanitize(baseArg1, tparams1, args1)
1771+
case nil =>
1772+
baseArg
1773+
}
1774+
base match {
1775+
case base @ AppliedType(tycon, baseArgs) =>
1776+
base.derivedAppliedType(tycon, baseArgs.mapconserve(sanitize(_, tparams, args)))
1777+
case _ =>
1778+
base
1779+
}
1780+
}
1781+
17301782
def recur(tp: Type): Type = try {
17311783
tp match {
17321784
case tp: CachedType =>
@@ -1790,7 +1842,10 @@ object SymDenotations {
17901842
case LambdaParam(_, _) :: _ =>
17911843
recur(tp.superType)
17921844
case tparams: List[Symbol @unchecked] =>
1793-
recur(tycon).subst(tparams, args)
1845+
var base = recur(tycon)
1846+
if (args.exists(_.isInstanceOf[TypeBounds]))
1847+
base = handleWildcards(base, tparams, args)
1848+
base.subst(tparams, args)
17941849
}
17951850
record(tp, baseTp)
17961851
baseTp

tests/pos/i5844.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait A
2+
trait B[X, Y]{
3+
def m(): X
4+
}
5+
trait C[X] extends B[X, X & A]
6+
7+
object O{
8+
def m(c: C[_]) = {
9+
val x = c.m()
10+
}
11+
}

0 commit comments

Comments
 (0)