Skip to content

Commit ac19a03

Browse files
committed
Fix bounds propagation
The previous type parameter representation in terms of type members achieved bounds propagation by waiting until a type member was selected and then taking original bounds and refinements together as its info. This no longer works with explicit applications. Instead, we need to propagate bounds into wildcard arguments explicitly, when a type application is created. Also, fix argument computation in asSeenFrom
1 parent 182e590 commit ac19a03

File tree

2 files changed

+69
-35
lines changed

2 files changed

+69
-35
lines changed

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,51 @@ class TypeApplications(val self: Type) extends AnyVal {
405405
}
406406
case nil => t
407407
}
408+
val stripped = self.stripTypeVar
409+
val dealiased = stripped.safeDealias
410+
411+
/** Normalize a TypeBounds argument. This involves (1) propagating bounds
412+
* from the type parameters into the argument, (2) possibly choosing the argument's
413+
* upper or lower bound according to variance.
414+
*
415+
* Bounds propagation works as follows: If the dealiased type constructor is a TypeRef
416+
* `p.c`,
417+
* - take the type paramater bounds of `c` as seen from `p`,
418+
* - substitute concrete (non-bound) arguments for corresponding formal type parameters,
419+
* - interpolate, so that any (F-bounded) type parameters in the resulting bounds are avoided.
420+
* The resulting bounds are joined (via &) with corresponding type bound arguments.
421+
*/
408422
def normalizeWildcardArg(arg: Type, tparam: TypeParamInfo): Type = arg match {
409423
case TypeBounds(lo, hi) =>
410424
val v = tparam.paramVariance
411-
if (v > 0) hi else if (v < 0) lo else arg
425+
val avoidParams = new ApproximatingTypeMap {
426+
variance = v
427+
def apply(t: Type) = t match {
428+
case t: TypeRef if typParams contains t.symbol =>
429+
val bounds = apply(t.info).bounds
430+
range(bounds.lo, bounds.hi)
431+
case _ => mapOver(t)
432+
}
433+
}
434+
val pbounds = dealiased match {
435+
case dealiased @ TypeRef(prefix, _) =>
436+
val (concreteArgs, concreteParams) = // @!!! optimize?
437+
args.zip(typeParams.asInstanceOf[List[TypeSymbol]])
438+
.filter(!_._1.isInstanceOf[TypeBounds])
439+
.unzip
440+
avoidParams(
441+
tparam.paramInfo.asSeenFrom(prefix, tparam.asInstanceOf[TypeSymbol].owner)
442+
.subst(concreteParams, concreteArgs)).orElse(TypeBounds.empty)
443+
case _ =>
444+
TypeBounds.empty
445+
}
446+
//typr.println(i"normalize arg $arg for $tparam in $self app $args%, %, pbounds, = $pbounds")
447+
if (v > 0) hi & pbounds.hiBound
448+
else if (v < 0) lo | pbounds.loBound
449+
else arg & pbounds
412450
case _ => arg
413451
}
414-
val stripped = self.stripTypeVar
415-
val dealiased = stripped.safeDealias
452+
416453
if (args.isEmpty || ctx.erasedTypes) self
417454
else dealiased match {
418455
case dealiased: HKTypeLambda =>

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

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,47 +46,44 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
4646
toPrefix(pre.select(nme.PACKAGE), cls, thiscls)
4747
else
4848
toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls)
49-
}
49+
}
5050
}
5151

52-
def argForParam(pre: Type, tparam: Symbol): Type = {
53-
val tparamCls = tparam.owner
54-
pre.baseType(tparamCls) match {
55-
case AppliedType(_, allArgs) =>
56-
var tparams = tparamCls.typeParams
57-
var args = allArgs
58-
var idx = 0
59-
while (tparams.nonEmpty && args.nonEmpty) {
60-
if (tparams.head.eq(tparam))
61-
return args.head match {
62-
case bounds: TypeBounds =>
63-
val v = variance
64-
if (v > 0) bounds.hi
65-
else if (v < 0) bounds.lo
66-
else TypeArgRef(pre, cls.typeRef, idx)
67-
case arg => arg
68-
}
69-
tparams = tparams.tail
70-
args = args.tail
71-
idx += 1
72-
}
73-
throw new AssertionError(ex"$pre contains no matching argument for ${tparam.showLocated} ")
74-
case OrType(tp1, tp2) => argForParam(tp1, cls) | argForParam(tp2, cls)
75-
case AndType(tp1, tp2) => argForParam(tp1, cls) & argForParam(tp2, cls)
76-
case _ => tp
52+
def argForParam(pre: Type, tparam: Symbol): Type = {
53+
val tparamCls = tparam.owner
54+
pre.baseType(tparamCls) match {
55+
case AppliedType(_, allArgs) =>
56+
var tparams = tparamCls.typeParams
57+
var args = allArgs
58+
var idx = 0
59+
while (tparams.nonEmpty && args.nonEmpty) {
60+
if (tparams.head.eq(tparam))
61+
return args.head match {
62+
case bounds: TypeBounds =>
63+
val v = variance
64+
if (v > 0) bounds.hi
65+
else if (v < 0) bounds.lo
66+
else TypeArgRef(pre, cls.typeRef, idx)
67+
case arg => arg
68+
}
69+
tparams = tparams.tail
70+
args = args.tail
71+
idx += 1
72+
}
73+
throw new AssertionError(ex"$pre contains no matching argument for ${tparam.showLocated} ")
74+
case OrType(tp1, tp2) => argForParam(tp1, cls) | argForParam(tp2, cls)
75+
case AndType(tp1, tp2) => argForParam(tp1, cls) & argForParam(tp2, cls)
76+
case _ => tp
77+
}
7778
}
78-
}
7979

8080
/*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG
8181
tp match {
8282
case tp: NamedType =>
8383
val sym = tp.symbol
8484
if (sym.isStatic) tp
85-
else {
86-
val pre1 = atVariance(variance max 0)(this(tp.prefix))
87-
if (Config.newScheme && sym.is(TypeParam)) argForParam(pre1, sym)
88-
else derivedSelect(tp, pre1)
89-
}
85+
else if (Config.newScheme && sym.is(TypeParam)) argForParam(pre, sym)
86+
else derivedSelect(tp, atVariance(variance max 0)(this(tp.prefix)))
9087
case tp: ThisType =>
9188
toPrefix(pre, cls, tp.cls)
9289
case _: BoundType | NoPrefix =>

0 commit comments

Comments
 (0)