Skip to content

Commit 84a1a7a

Browse files
committed
Avoid dealiasing on type application
When applying a type alias of a type lambda, keep the original application instead of reducing. But reduce anyway if - the reduced type is an application where the type constructor has the same kind as the original type constructor, or - some of the arguments are wildcards.
1 parent cdebd91 commit 84a1a7a

10 files changed

+103
-66
lines changed

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,19 +300,21 @@ trait ConstraintHandling {
300300
* to refer to such a lambda parameter because the lambda parameter is
301301
* not visible where `A` is defined. Consequently, we need to
302302
* approximate the bound so that the lambda parameter does not appear in it.
303-
* Test case in neg/i94-nada.scala. This test crashes with an illegal instance
304-
* error when the rest of the SI-2712 fix is applied but `pruneLambdaParams` is
303+
* If `tp` is an upper bound, we need to approximate with something smaller,
304+
* otherwise something larger.
305+
* Test case in pos/i94-nada.scala. This test crashes with an illegal instance
306+
* error in Test2 when the rest of the SI-2712 fix is applied but `pruneLambdaParams` is
305307
* missing.
306308
*/
307309
def pruneLambdaParams(tp: Type) =
308-
if (comparingLambdas) {
310+
if (comparingLambdas && param.binder.isInstanceOf[PolyType]) {
309311
val approx = new ApproximatingTypeMap {
310312
def apply(t: Type): Type = t match {
311313
case t @ PolyParam(tl: TypeLambda, n) =>
312314
val effectiveVariance = if (fromBelow) -variance else variance
313315
val bounds = tl.paramBounds(n)
314-
if (effectiveVariance > 0) bounds.hi
315-
else if (effectiveVariance < 0 ) bounds.lo
316+
if (effectiveVariance > 0) bounds.lo
317+
else if (effectiveVariance < 0) bounds.hi
316318
else NoType
317319
case _ =>
318320
mapOver(t)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ object Symbols {
491491

492492
// TypeParamInfo methods
493493
def isTypeParam(implicit ctx: Context) = denot.is(TypeParam)
494-
def paramName(implicit ctx: Context): Name = name
494+
def paramName(implicit ctx: Context) = name.asTypeName
495495
def paramBounds(implicit ctx: Context) = denot.info.bounds
496496
def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds
497497
def paramBoundsOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter

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

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,10 @@ class TypeApplications(val self: Type) extends AnyVal {
338338
*
339339
* TODO: Handle parameterized lower bounds
340340
*/
341-
def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = {
341+
def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = {
342342
def expand(tp: Type) =
343343
TypeLambda(
344-
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.paramVariance))(
344+
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.variance))(
345345
tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
346346
tl => tl.lifted(tparams, tp))
347347
assert(!isHK, self)
@@ -439,20 +439,13 @@ class TypeApplications(val self: Type) extends AnyVal {
439439
}
440440
}
441441

442-
/** Encode
442+
/** The type representing
443443
*
444444
* T[U1, ..., Un]
445445
*
446446
* where
447447
* @param self = `T`
448448
* @param args = `U1,...,Un`
449-
* performing the following simplifications
450-
*
451-
* 1. If `T` is an eta expansion `[X1,..,Xn] -> C[X1,...,Xn]` of class `C` compute
452-
* `C[U1, ..., Un]` instead.
453-
* 2. If `T` is some other type lambda `[X1,...,Xn] -> S` none of the arguments
454-
* `U1,...,Un` is a wildcard, compute `[X1:=U1, ..., Xn:=Un]S` instead.
455-
* 3. If `T` is a polytype, instantiate it to `U1,...,Un`.
456449
*/
457450
final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ {
458451
val typParams = self.typeParams
@@ -469,30 +462,52 @@ class TypeApplications(val self: Type) extends AnyVal {
469462
}
470463
case nil => t
471464
}
465+
val stripped = self.stripTypeVar
466+
val dealiased = stripped.safeDealias
472467
if (args.isEmpty || ctx.erasedTypes) self
473-
else self.stripTypeVar.safeDealias match {
474-
case self: TypeLambda =>
475-
if (!args.exists(_.isInstanceOf[TypeBounds])) self.instantiate(args)
476-
else {
477-
val reducer = new Reducer(self, args)
478-
val reduced = reducer(self.resType)
479-
if (reducer.allReplaced) reduced
480-
else HKApply(self, args)
481-
}
482-
case self: PolyType =>
483-
self.instantiate(args)
484-
case self: AndOrType =>
485-
self.derivedAndOrType(self.tp1.appliedTo(args), self.tp2.appliedTo(args))
486-
case self: TypeAlias =>
487-
self.derivedTypeAlias(self.alias.appliedTo(args))
488-
case self: TypeBounds =>
489-
self.derivedTypeBounds(self.lo, self.hi.appliedTo(args))
490-
case self: LazyRef =>
491-
LazyRef(() => self.ref.appliedTo(args))
492-
case self: WildcardType =>
493-
self
494-
case self: TypeRef if self.symbol == defn.NothingClass =>
495-
self
468+
else dealiased match {
469+
case dealiased: TypeLambda =>
470+
def tryReduce =
471+
if (!args.exists(_.isInstanceOf[TypeBounds])) {
472+
val reduced = dealiased.instantiate(args)
473+
if (dealiased eq stripped) reduced
474+
else reduced match {
475+
case AppliedType(tycon, args) if variancesConform(typParams, tycon.typeParams) =>
476+
// Reducing is safe for type inference, as kind of type constructor does not change
477+
//println(i"reduced: $reduced instead of ${HKApply(self, args)}")
478+
reduced
479+
case _ =>
480+
// Reducing changes kind, keep hk application instead
481+
//println(i"fallback: ${HKApply(self, args)} instead of $reduced")
482+
HKApply(self, args)
483+
}
484+
}
485+
else dealiased.resType match {
486+
case AppliedType(tycon, args1) if tycon.safeDealias ne tycon =>
487+
dealiased
488+
.derivedTypeLambda(resType = tycon.safeDealias.appliedTo(args1))
489+
.appliedTo(args)
490+
case _ =>
491+
val reducer = new Reducer(dealiased, args)
492+
val reduced = reducer(dealiased.resType)
493+
if (reducer.allReplaced) reduced
494+
else HKApply(dealiased, args)
495+
}
496+
tryReduce
497+
case dealiased: PolyType =>
498+
dealiased.instantiate(args)
499+
case dealiased: AndOrType =>
500+
dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args))
501+
case dealiased: TypeAlias =>
502+
dealiased.derivedTypeAlias(dealiased.alias.appliedTo(args))
503+
case dealiased: TypeBounds =>
504+
dealiased.derivedTypeBounds(dealiased.lo, dealiased.hi.appliedTo(args))
505+
case dealiased: LazyRef =>
506+
LazyRef(() => dealiased.ref.appliedTo(args))
507+
case dealiased: WildcardType =>
508+
dealiased
509+
case dealiased: TypeRef if dealiased.symbol == defn.NothingClass =>
510+
dealiased
496511
case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] =>
497512
HKApply(self, args)
498513
case dealiased =>

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
628628
val tparams1 = tparams1a.drop(lengthDiff)
629629
variancesConform(tparams1, tparams) && {
630630
if (lengthDiff > 0)
631-
tycon1b = tycon1a
632-
.appliedTo(args1.take(lengthDiff) ++ tparams1.map(_.paramRef))
633-
.LambdaAbstract(tparams1)
631+
tycon1b = TypeLambda(tparams1.map(_.paramName), tparams1.map(_.paramVariance))(
632+
tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
633+
tl => tycon1a.appliedTo(args1.take(lengthDiff) ++
634+
tparams1.indices.toList.map(PolyParam(tl, _))))
634635
(ctx.mode.is(Mode.TypevarsMissContext) ||
635636
tryInstantiate(tycon2, tycon1b.ensureHK)) &&
636637
isSubType(tp1, tycon1b.appliedTo(args2))

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dotty.tools.dotc.core
22

3-
import Names.Name
3+
import Names.TypeName
44
import Contexts.Context
55
import Types.{Type, TypeBounds}
66

@@ -15,22 +15,22 @@ trait TypeParamInfo {
1515
def isTypeParam(implicit ctx: Context): Boolean
1616

1717
/** The name of the type parameter */
18-
def paramName(implicit ctx: Context): Name
18+
def paramName(implicit ctx: Context): TypeName
1919

2020
/** The info of the type parameter */
2121
def paramBounds(implicit ctx: Context): TypeBounds
2222

2323
/** The info of the type parameter as seen from a prefix type.
2424
* For type parameter symbols, this is the `memberInfo` as seen from `prefix`.
25-
* For type lambda parameters, it's the same as `paramBounds` as
25+
* For type lambda parameters, it's the same as `paramBounds` as
2626
* `asSeenFrom` has already been applied to the whole type lambda.
2727
*/
2828
def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds
29-
29+
3030
/** The parameter bounds, or the completer if the type parameter
3131
* is an as-yet uncompleted symbol.
3232
*/
33-
def paramBoundsOrCompleter(implicit ctx: Context): Type
33+
def paramBoundsOrCompleter(implicit ctx: Context): Type
3434

3535
/** The variance of the type parameter */
3636
def paramVariance(implicit ctx: Context): Int

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ object Types {
117117
case _ => this1.symbol eq sym
118118
}
119119
case this1: RefinedOrRecType => this1.parent.isRef(sym)
120+
case this1: HKApply => this1.superType.isRef(sym)
120121
case _ => false
121122
}
122123

@@ -857,6 +858,10 @@ object Types {
857858
tp.derivedAnnotatedType(tp.tpe.dealias, tp.annot)
858859
case tp: LazyRef =>
859860
tp.ref.dealias
861+
case app @ HKApply(tycon, args) =>
862+
val tycon1 = tycon.dealias
863+
if (tycon1 ne tycon) app.superType.dealias
864+
else this
860865
case _ => this
861866
}
862867

@@ -2586,7 +2591,7 @@ object Types {
25862591
lazy val typeParams: List[LambdaParam] =
25872592
paramNames.indices.toList.map(new LambdaParam(this, _))
25882593

2589-
def derivedTypeLambda(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
2594+
def derivedTypeLambda(paramNames: List[TypeName] = paramNames, paramBounds: List[TypeBounds] = paramBounds, resType: Type)(implicit ctx: Context): Type =
25902595
resType match {
25912596
case resType @ TypeAlias(alias) =>
25922597
resType.derivedTypeAlias(duplicate(paramNames, paramBounds, alias))
@@ -2640,12 +2645,21 @@ object Types {
26402645
abstract case class HKApply(tycon: Type, args: List[Type])
26412646
extends CachedProxyType with ValueType {
26422647

2648+
private var validSuper: Period = Nowhere
2649+
private var cachedSuper: Type = _
2650+
26432651
override def underlying(implicit ctx: Context): Type = tycon
26442652

2645-
override def superType(implicit ctx: Context): Type = tycon match {
2646-
case tp: TypeLambda => defn.AnyType
2647-
case tp: TypeProxy => tp.superType.applyIfParameterized(args)
2648-
case _ => defn.AnyType
2653+
override def superType(implicit ctx: Context): Type = {
2654+
if (ctx.period != validSuper) {
2655+
cachedSuper = tycon match {
2656+
case tp: TypeLambda => defn.AnyType
2657+
case tp: TypeProxy => tp.superType.applyIfParameterized(args)
2658+
case _ => defn.AnyType
2659+
}
2660+
validSuper = ctx.period
2661+
}
2662+
cachedSuper
26492663
}
26502664
/*
26512665
def lowerBound(implicit ctx: Context): Type = tycon.stripTypeVar match {
@@ -2760,7 +2774,11 @@ object Types {
27602774
else bounds(paramNum)
27612775
}
27622776
// no customized hashCode/equals needed because cycle is broken in PolyType
2763-
override def toString = s"PolyParam($paramName)"
2777+
override def toString =
2778+
try s"PolyParam($paramName)"
2779+
catch {
2780+
case ex: IndexOutOfBoundsException => s"PolyParam(<bad index: $paramNum>)"
2781+
}
27642782

27652783
override def computeHash = doHash(paramNum, binder.identityHash)
27662784

tests/neg/boundspropagation.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ object test4 {
4040
}
4141

4242
class Test5 {
43-
"": ({ type U = this.type })#U // error // error
43+
"": ({ type U = this.type })#U // error
4444
}

tests/neg/i94-nada.scala

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/pos/i94-nada.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ trait Test2 {
3535
case class Left[A,B](x: A) extends Either[A,B] with Monad[A]
3636
case class Right[A,B](x: B) extends Either[A,B] with Monad[B]
3737
def flatMap[X,Y,M[X]](m: M[X], f: X => M[Y]): M[Y]
38-
println(flatMap(Right(1), {x: Int => Right(x)}))
38+
println(flatMap(Left(1), {x: Int => Left(x)}))
3939
}
4040
trait Test3 {
4141
def flatMap[X,Y,M[X]](m: M[X], f: X => M[Y]): M[Y]

tests/pos/t2712-6.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package test
2+
3+
object Tags {
4+
type Tagged[A, T] = {type Tag = T; type Self = A}
5+
6+
type @@[T, Tag] = Tagged[T, Tag]
7+
8+
trait Disjunction
9+
10+
def meh[M[_], A](ma: M[A]): M[A] = ma
11+
meh(null: Int @@ Disjunction)//.asInstanceOf[Int @@ Disjunction])
12+
}

0 commit comments

Comments
 (0)