Skip to content

Commit 5733684

Browse files
committed
Merge pull request #238 from dotty-staging/fix/bounds-propagation-v2
Fix/bounds propagation v2
2 parents 779afd2 + a2884d5 commit 5733684

23 files changed

+406
-164
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ object SymDenotations {
432432
final def isSetter(implicit ctx: Context) =
433433
(this is Accessor) &&
434434
originalName.isSetterName &&
435-
info.firstParamTypes.nonEmpty // to avoid being fooled by var x_= : Unit = ...
435+
(!isCompleted || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ...
436436

437437
/** is this the constructor of a class? */
438438
final def isClassConstructor = name == nme.CONSTRUCTOR

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,9 @@ class TypeApplications(val self: Type) extends AnyVal {
293293
/** If this is an encoding of a (partially) applied type, return its arguments,
294294
* otherwise return Nil.
295295
* Existential types in arguments are returned as TypeBounds instances.
296+
* @param interpolate See argInfo
296297
*/
297-
final def argInfos(implicit ctx: Context): List[Type] = {
298+
final def argInfos(interpolate: Boolean)(implicit ctx: Context): List[Type] = {
298299
var tparams: List[TypeSymbol] = null
299300
def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match {
300301
case tp @ RefinedType(tycon, name) =>
@@ -304,7 +305,7 @@ class TypeApplications(val self: Type) extends AnyVal {
304305
if (tparams == null) tparams = tycon.typeParams
305306
if (buf.size < tparams.length) {
306307
val tparam = tparams(buf.size)
307-
if (name == tparam.name) buf += tp.refinedInfo.argInfo(tparam)
308+
if (name == tparam.name) buf += tp.refinedInfo.argInfo(tparam, interpolate)
308309
else null
309310
} else null
310311
}
@@ -316,6 +317,8 @@ class TypeApplications(val self: Type) extends AnyVal {
316317
if (buf == null) Nil else buf.toList
317318
}
318319

320+
final def argInfos(implicit ctx: Context): List[Type] = argInfos(interpolate = true)
321+
319322
/** Argument types where existential types in arguments are disallowed */
320323
def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds
321324

@@ -338,16 +341,27 @@ class TypeApplications(val self: Type) extends AnyVal {
338341

339342
/** If this is the image of a type argument to type parameter `tparam`,
340343
* recover the type argument, otherwise NoType.
344+
* @param interpolate If true, replace type bounds as arguments corresponding to
345+
* variant type parameters by their dominating element. I.e. an argument
346+
*
347+
* T <: U
348+
*
349+
* for a covariant type-parameter becomes U, and an argument
350+
*
351+
* T >: L
352+
*
353+
* for a contravariant type-parameter becomes L.
341354
*/
342-
final def argInfo(tparam: Symbol)(implicit ctx: Context): Type = self match {
355+
final def argInfo(tparam: Symbol, interpolate: Boolean = true)(implicit ctx: Context): Type = self match {
343356
case TypeBounds(lo, hi) =>
344357
if (lo eq hi) hi
345-
else {
358+
else if (interpolate) {
346359
val v = tparam.variance
347360
if (v > 0 && (lo isRef defn.NothingClass)) hi
348361
else if (v < 0 && (hi isRef defn.AnyClass)) lo
349-
else self // it's wildcard type; return its bounds
362+
else self
350363
}
364+
else self
351365
case _ =>
352366
NoType
353367
}

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ class TypeComparer(initctx: Context) extends DotClass {
307307
}
308308
}
309309

310+
private def narrowRefined(tp: Type): Type = tp match {
311+
case tp: RefinedType => RefinedThis(tp)
312+
case _ => tp
313+
}
314+
310315
/** If the prefix of a named type is `this` (i.e. an instance of type
311316
* `ThisType` or `RefinedThis`), and there is a refinement type R that
312317
* "refines" (transitively contains as its parent) a class reference
@@ -650,52 +655,54 @@ class TypeComparer(initctx: Context) extends DotClass {
650655
}
651656
compareNamed
652657
case tp2 @ RefinedType(parent2, name2) =>
658+
def qualifies(m: SingleDenotation) = isSubType(m.info, tp2.refinedInfo)
659+
def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance
660+
case mbr: SingleDenotation => qualifies(mbr)
661+
case _ => mbr hasAltWith qualifies
662+
}
663+
def compareRefinedSlow: Boolean = {
664+
def hasMatchingMember(name: Name): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($name) ${tp1.member(name).info.show}", subtyping) /*<|<*/ {
665+
val tp1r = rebaseQual(tp1, name)
666+
(memberMatches(narrowRefined(tp1r) member name)
667+
||
668+
{ // special case for situations like:
669+
// foo <: C { type T = foo.T }
670+
tp2.refinedInfo match {
671+
case TypeBounds(lo, hi) if lo eq hi =>
672+
!ctx.phase.erasedTypes && (tp1r select name) =:= lo
673+
case _ => false
674+
}
675+
})
676+
}
677+
val matchesParent = {
678+
val saved = pendingRefinedBases
679+
try {
680+
addPendingName(name2, tp2, tp2)
681+
isSubType(tp1, parent2)
682+
} finally pendingRefinedBases = saved
683+
}
684+
(matchesParent && (
685+
name2 == nme.WILDCARD
686+
|| hasMatchingMember(name2)
687+
|| fourthTry(tp1, tp2))
688+
|| needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2)))
689+
}
653690
def compareRefined: Boolean = tp1.widen match {
654691
case tp1 @ RefinedType(parent1, name1) if name1 == name2 && name1.isTypeName =>
655-
// optimized case; all info on tp1.name1 is in refinement tp1.refinedInfo.
656-
isSubType(normalizedInfo(tp1), tp2.refinedInfo) && {
657-
val saved = pendingRefinedBases
658-
try {
659-
addPendingName(name1, tp1, tp2)
660-
isSubType(parent1, parent2)
661-
}
662-
finally pendingRefinedBases = saved
692+
normalizedInfo(tp1) match {
693+
case bounds1 @ TypeBounds(lo1, hi1) if lo1 eq hi1 =>
694+
isSubType(bounds1, tp2.refinedInfo) && {
695+
val saved = pendingRefinedBases
696+
try {
697+
addPendingName(name1, tp1, tp2)
698+
isSubType(parent1, parent2)
699+
} finally pendingRefinedBases = saved
700+
}
701+
case _ =>
702+
compareRefinedSlow
663703
}
664704
case _ =>
665-
def qualifies(m: SingleDenotation) = isSubType(m.info, tp2.refinedInfo)
666-
def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance
667-
case mbr: SingleDenotation => qualifies(mbr)
668-
case _ => mbr hasAltWith qualifies
669-
}
670-
def hasMatchingMember(name: Name): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($name) ${tp1.member(name).info.show}", subtyping) /*<|<*/ {
671-
val tp1r = rebaseQual(tp1, name)
672-
( memberMatches(tp1r member name)
673-
||
674-
{ // special case for situations like:
675-
// foo <: C { type T = foo.T }
676-
tp2.refinedInfo match {
677-
case TypeBounds(lo, hi) if lo eq hi =>
678-
!ctx.phase.erasedTypes && (tp1r select name) =:= lo
679-
case _ => false
680-
}
681-
}
682-
)
683-
}
684-
val matchesParent = {
685-
val saved = pendingRefinedBases
686-
try {
687-
addPendingName(name2, tp2, tp2)
688-
isSubType(tp1, parent2)
689-
}
690-
finally pendingRefinedBases = saved
691-
}
692-
( matchesParent && (
693-
name2 == nme.WILDCARD
694-
|| hasMatchingMember(name2)
695-
|| fourthTry(tp1, tp2)
696-
)
697-
|| needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2))
698-
)
705+
compareRefinedSlow
699706
}
700707
compareRefined
701708
case OrType(tp21, tp22) =>

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import config.Printers._
77
import Decorators._
88
import StdNames._
99
import util.SimpleMap
10+
import collection.mutable
11+
import ast.tpd._
1012

1113
trait TypeOps { this: Context =>
1214

1315
final def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = {
1416

15-
def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.debugTraceIndented(s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ {
17+
def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ {
1618
if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass))
1719
tp
1820
else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists)
@@ -24,7 +26,7 @@ trait TypeOps { this: Context =>
2426
toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls)
2527
}
2628

27-
/*>|>*/ ctx.conditionalTraceIndented(TypeOps.track , s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG
29+
/*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG
2830
tp match {
2931
case tp: NamedType =>
3032
val sym = tp.symbol
@@ -56,7 +58,13 @@ trait TypeOps { this: Context =>
5658
final def simplify(tp: Type, theMap: SimplifyMap): Type = tp match {
5759
case tp: NamedType =>
5860
if (tp.symbol.isStatic) tp
59-
else tp.derivedSelect(simplify(tp.prefix, theMap))
61+
else tp.derivedSelect(simplify(tp.prefix, theMap)) match {
62+
case tp1: NamedType if tp1.denotationIsCurrent =>
63+
val tp2 = tp1.reduceProjection
64+
//if (tp2 ne tp1) println(i"simplified $tp1 -> $tp2")
65+
tp2
66+
case tp1 => tp1
67+
}
6068
case tp: PolyParam =>
6169
typerState.constraint.typeVarOfParam(tp) orElse tp
6270
case _: ThisType | _: BoundType | NoPrefix =>
@@ -301,6 +309,40 @@ trait TypeOps { this: Context =>
301309
parentRefs
302310
}
303311

312+
/** An argument bounds violation is a triple consisting of
313+
* - the argument tree
314+
* - a string "upper" or "lower" indicating which bound is violated
315+
* - the violated bound
316+
*/
317+
type BoundsViolation = (Tree, String, Type)
318+
319+
/** The list of violations where arguments are not within bounds.
320+
* @param args The arguments
321+
* @param boundss The list of type bounds
322+
* @param instantiate A function that maps a bound type and the list of argument types to a resulting type.
323+
* Needed to handle bounds that refer to other bounds.
324+
*/
325+
def boundsViolations(args: List[Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): List[BoundsViolation] = {
326+
val argTypes = args.tpes
327+
val violations = new mutable.ListBuffer[BoundsViolation]
328+
for ((arg, bounds) <- args zip boundss) {
329+
def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
330+
//println(i"instantiating ${bounds.hi} with $argTypes")
331+
//println(i" = ${instantiate(bounds.hi, argTypes)}")
332+
val hiBound = instantiate(bounds.hi, argTypes.mapConserve(_.bounds.hi))
333+
// Note that argTypes can contain a TypeBounds type for arguments that are
334+
// not fully determined. In that case we need to check against the hi bound of the argument.
335+
if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound))
336+
if (!(bounds.lo <:< hi)) violations += ((arg, "lower", bounds.lo))
337+
}
338+
arg.tpe match {
339+
case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
340+
case tp => checkOverlapsBounds(tp, tp)
341+
}
342+
}
343+
violations.toList
344+
}
345+
304346
/** Is `feature` enabled in class `owner`?
305347
* This is the case if one of the following two alternatives holds:
306348
*

0 commit comments

Comments
 (0)