Skip to content

Commit a6b6c08

Browse files
committed
Fix bounds checking of hk applied typed
Previous logic could only handle classes as constructors. Also, address other reviewers comments.
1 parent 18b3080 commit a6b6c08

File tree

8 files changed

+70
-56
lines changed

8 files changed

+70
-56
lines changed

src/dotty/tools/dotc/config/Config.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ object Config {
7272
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
7373
final val fastPathForRefinedSubtype = true
7474

75-
/** If this flag is set, $apply projections are checked that they apply to a
76-
* higher-kinded type.
75+
/** If this flag is set, higher-kinded applications are checked for validity
7776
*/
78-
final val checkProjections = false
77+
final val checkHKApplications = false
7978

8079
/** The recursion depth for showing a summarized string */
8180
final val summarizeDepth = 2

src/dotty/tools/dotc/config/Printers.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ object Printers {
1515
val default: Printer = new Printer
1616
val dottydoc: Printer = noPrinter
1717
val core: Printer = noPrinter
18-
val typr: Printer = noPrinter
19-
val constr: Printer = noPrinter
18+
val typr: Printer = new Printer
19+
val constr: Printer = new Printer
2020
val checks: Printer = noPrinter
2121
val overload: Printer = noPrinter
2222
val implicits: Printer = noPrinter
2323
val implicitsDetailed: Printer = noPrinter
24-
val subtyping: Printer = noPrinter
24+
val subtyping: Printer = new Printer
2525
val unapp: Printer = noPrinter
2626
val gadts: Printer = noPrinter
2727
val hk: Printer = noPrinter

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,12 @@ class TypeApplications(val self: Type) extends AnyVal {
338338
*
339339
* TODO: Handle parameterized lower bounds
340340
*/
341-
def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = {
341+
def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = {
342342
def expand(tp: Type) =
343343
TypeLambda(
344-
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.variance))(
344+
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.paramVariance))(
345345
tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
346346
tl => tl.lifted(tparams, tp))
347-
assert(!isHK, self)
348347
self match {
349348
case self: TypeAlias =>
350349
self.derivedTypeAlias(expand(self.alias))
@@ -489,6 +488,8 @@ class TypeApplications(val self: Type) extends AnyVal {
489488
}
490489
else dealiased.resType match {
491490
case AppliedType(tycon, args1) if tycon.safeDealias ne tycon =>
491+
// In this case we should always dealias since we cannot handle
492+
// higher-kinded applications to wildcard arguments.
492493
dealiased
493494
.derivedTypeLambda(resType = tycon.safeDealias.appliedTo(args1))
494495
.appliedTo(args)

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

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ object Types {
517517
def goApply(tp: HKApply) = tp.tycon match {
518518
case tl: TypeLambda =>
519519
go(tl.resType).mapInfo(info =>
520-
tl.derivedTypeLambda(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args))
520+
tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args))
521521
case _ =>
522522
go(tp.superType)
523523
}
@@ -879,12 +879,6 @@ object Types {
879879
case _ => this
880880
}
881881

882-
/** If this is a TypeAlias type, its alias, otherwise this type itself */
883-
final def followTypeAlias(implicit ctx: Context): Type = this match {
884-
case TypeAlias(alias) => alias
885-
case _ => this
886-
}
887-
888882
/** If this is a (possibly aliased, annotated, and/or parameterized) reference to
889883
* a class, the class type ref, otherwise NoType.
890884
* @param refinementOK If `true` we also skip non-parameter refinements.
@@ -1923,13 +1917,9 @@ object Types {
19231917
}
19241918

19251919
object TypeRef {
1926-
def checkProjection(prefix: Type, name: TypeName)(implicit ctx: Context) = ()
1927-
19281920
/** Create type ref with given prefix and name */
1929-
def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = {
1930-
if (Config.checkProjections) checkProjection(prefix, name)
1921+
def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef =
19311922
ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef]
1932-
}
19331923

19341924
/** Create type ref to given symbol */
19351925
def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
@@ -1938,10 +1928,8 @@ object Types {
19381928
/** Create a non-member type ref (which cannot be reloaded using `member`),
19391929
* with given prefix, name, and symbol.
19401930
*/
1941-
def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = {
1942-
if (Config.checkProjections) checkProjection(prefix, name)
1931+
def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
19431932
unique(new TypeRefWithFixedSym(prefix, name, sym))
1944-
}
19451933

19461934
/** Create a type ref referring to given symbol with given name.
19471935
* This is very similar to TypeRef(Type, Symbol),
@@ -2057,9 +2045,7 @@ object Types {
20572045
private def badInst =
20582046
throw new AssertionError(s"bad instantiation: $this")
20592047

2060-
def checkInst(implicit ctx: Context): this.type = {
2061-
this
2062-
}
2048+
def checkInst(implicit ctx: Context): this.type = this // debug hook
20632049

20642050
def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): Type =
20652051
if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this
@@ -2139,7 +2125,7 @@ object Types {
21392125
override def computeHash = doHash(parent)
21402126
override def toString = s"RecType($parent | $hashCode)"
21412127

2142-
private def checkInst(implicit ctx: Context): this.type = this
2128+
private def checkInst(implicit ctx: Context): this.type = this // debug hook
21432129
}
21442130

21452131
object RecType {
@@ -2550,8 +2536,8 @@ object Types {
25502536
case _ => false
25512537
}
25522538

2553-
def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context) =
2554-
derivedGenericType(paramNames, paramBounds, resType)
2539+
def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType =
2540+
derivedGenericType(paramNames, paramBounds, resType).asInstanceOf[PolyType]
25552541

25562542
def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): PolyType =
25572543
PolyType(paramNames)(
@@ -2591,7 +2577,7 @@ object Types {
25912577
lazy val typeParams: List[LambdaParam] =
25922578
paramNames.indices.toList.map(new LambdaParam(this, _))
25932579

2594-
def derivedTypeLambda(paramNames: List[TypeName] = paramNames, paramBounds: List[TypeBounds] = paramBounds, resType: Type)(implicit ctx: Context): Type =
2580+
def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
25952581
resType match {
25962582
case resType @ TypeAlias(alias) =>
25972583
resType.derivedTypeAlias(duplicate(paramNames, paramBounds, alias))
@@ -2600,9 +2586,12 @@ object Types {
26002586
if (lo.isRef(defn.NothingClass)) lo else duplicate(paramNames, paramBounds, lo),
26012587
duplicate(paramNames, paramBounds, hi))
26022588
case _ =>
2603-
derivedGenericType(paramNames, paramBounds, resType)
2589+
derivedTypeLambda(paramNames, paramBounds, resType)
26042590
}
26052591

2592+
def derivedTypeLambda(paramNames: List[TypeName] = paramNames, paramBounds: List[TypeBounds] = paramBounds, resType: Type)(implicit ctx: Context): TypeLambda =
2593+
derivedGenericType(paramNames, paramBounds, resType).asInstanceOf[TypeLambda]
2594+
26062595
def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): TypeLambda =
26072596
TypeLambda(paramNames, variances)(
26082597
x => paramBounds mapConserve (_.subst(this, x).bounds),
@@ -2664,6 +2653,7 @@ object Types {
26642653
cachedSuper
26652654
}
26662655

2656+
/* (Not needed yet) */
26672657
def lowerBound(implicit ctx: Context) = tycon.stripTypeVar match {
26682658
case tycon: TypeRef =>
26692659
tycon.info match {
@@ -2676,13 +2666,6 @@ object Types {
26762666
NoType
26772667
}
26782668

2679-
/*
2680-
def lowerBound(implicit ctx: Context): Type = tycon.stripTypeVar match {
2681-
case tp: TypeRef =>
2682-
val lb = tp.info.bounds.lo.typeParams.length == args.lengt
2683-
case _ => defn.NothingType
2684-
}
2685-
*/
26862669
def typeParams(implicit ctx: Context): List[TypeParamInfo] = {
26872670
val tparams = tycon.typeParams
26882671
if (tparams.isEmpty) TypeLambda.any(args.length).typeParams else tparams
@@ -2705,7 +2688,7 @@ object Types {
27052688
case _ =>
27062689
assert(false, s"illegal type constructor in $this")
27072690
}
2708-
check(tycon)
2691+
if (Config.checkHKApplications) check(tycon)
27092692
this
27102693
}
27112694
}

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,16 @@ object Checking {
8383
case AppliedTypeTree(tycon, args) =>
8484
// If `args` is a list of named arguments, return corresponding type parameters,
8585
// otherwise return type parameters unchanged
86-
def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[Symbol] =
87-
if (hasNamedArg(args))
88-
for (NamedArg(name, _) <- args) yield tycon.tpe.member(name).symbol
89-
else
90-
tparams
91-
val tparams = matchNamed(tycon.tpe.typeSymbol.typeParams, args)
92-
val bounds = tparams.map(tparam =>
93-
tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds)
94-
checkBounds(args, bounds, _.substDealias(tparams, _))
86+
val tparams = tycon.tpe.typeParams
87+
def argNamed(tparam: TypeParamInfo) = args.find {
88+
case NamedArg(name, _) => name == tparam.paramName
89+
case _ => false
90+
}.getOrElse(TypeTree(tparam.paramRef))
91+
val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args
92+
val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe))
93+
def instantiate(bound: Type, args: List[Type]) =
94+
bound.LambdaAbstract(tparams).appliedTo(args)
95+
checkBounds(orderedArgs, bounds, instantiate)
9596

9697
def checkValidIfHKApply(implicit ctx: Context): Unit =
9798
checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos)

tests/neg/t7278.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ object Test {
1313

1414
def fail1(): Unit = {
1515
val b = new B
16-
var x1: EE[A] = null
17-
var x2: EE[B] = new b.E // old-error: found: B#E, required: A#E
16+
var x1: EE[A] = null // error: Type argument A does not conform to upper bound EC
17+
var x2: EE[B] = new b.E // error: Type argument B does not conform to upper bound EC
1818
// x1 = x2 // gives a prior type error: B#E, required: A#E, masked to get at the real thing.
1919
}
2020

@@ -27,8 +27,8 @@ object Test {
2727
}
2828
*/
2929
def fail3(): Unit = {
30-
var x1: EE[C] = 5
31-
var x2: EE[C & D] = ""
30+
var x1: EE[C] = 5 // error: Type argument C does not conform to upper bound EC
31+
var x2: EE[C & D] = "" // error: Type argument C & D does not conform to upper bound EC
3232
x1 = x2
3333
}
3434

tests/pos-scala2/t2994.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object Naturals {
2020

2121
// crashes scala-2.8.0 beta1
2222
trait MUL[n <: NAT, m <: NAT] extends NAT {
23-
trait curry[n[_, _], s[_]] { type f[z <: NAT] = n[s, z] }
23+
trait curry[n[_[_], _], s[_]] { type f[z <: NAT] = n[s, z] }
2424
type a[s[_ <: NAT] <: NAT, z <: NAT] = n#a[curry[m#a, s]#f, z]
2525
}
2626

tests/pos/hklower.scala

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
1-
class Test { // error: conflicting bounds
1+
class Test {
22

33
type T[X]
44
type U[X] = T[X]
55

66
type V[X] >: T[X]
77
type W[X] >: T[X] <: T[X]
88

9-
def f[C[X] >: T[X]]() = ???
9+
def f[C[X] >: T[X]](x: C[Int]) = ???
10+
11+
val v: V[Int] = ???
12+
val t: T[Int] = ???
13+
14+
f[V](v)
15+
16+
f[V](t)
17+
18+
19+
}
20+
class Test2 {
21+
22+
class T[X]
23+
type U[X] = T[X]
24+
25+
type V[X] >: T[X]
26+
type W[X] >: T[X] <: T[X]
27+
28+
def f[C[X] >: T[X]](x: C[Int]) = ???
29+
30+
val v: V[Int] = ???
31+
val t: T[Int] = ???
32+
33+
f[V](v)
34+
35+
f[V](t)
36+
37+
var x: V[Int] = _
38+
x = t
39+
1040

1141
}

0 commit comments

Comments
 (0)