Skip to content

Commit 4b83e2e

Browse files
committed
Fix computations of parameter bounds of HK lambdas
- Type parameters of hk lambdas depend on prefix, can't be computed from symbol if prefix is non-trivial - Parameter bounds of such type parameters are already computed, so can be retrieved with paramInfo. The doc comment of paramInfoAsSeenFrom indicates this, but the implementation did something different. - Typer and Checking need to call paramInfoAsSeenFrom to get the right bounds in general. This also fixes #4884.
1 parent 494473f commit 4b83e2e

File tree

6 files changed

+151
-7
lines changed

6 files changed

+151
-7
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait ParamInfo {
2727
* For type lambda parameters, it's the same as `paramInfos` as
2828
* `asSeenFrom` has already been applied to the whole type lambda.
2929
*/
30-
def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type
30+
def paramInfoAsSeenFrom(prefix: Type)(implicit ctx: Context): Type
3131

3232
/** The parameter bounds, or the completer if the type parameter
3333
* is an as-yet uncompleted symbol.

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,16 @@ class TypeApplications(val self: Type) extends AnyVal {
168168
* any type parameter that is-rebound by the refinement.
169169
*/
170170
final def typeParams(implicit ctx: Context): List[TypeParamInfo] = /*>|>*/ track("typeParams") /*<|<*/ {
171+
def isTrivial(prefix: Type, tycon: Symbol) = prefix match {
172+
case prefix: ThisType => prefix.cls `eq` tycon.owner
173+
case NoPrefix => true
174+
case _ => false
175+
}
171176
try self match {
172177
case self: TypeRef =>
173178
val tsym = self.symbol
174179
if (tsym.isClass) tsym.typeParams
175-
else if (!tsym.exists) self.info.typeParams
180+
else if (!tsym.exists || !isTrivial(self.prefix, tsym)) self.info.typeParams
176181
else tsym.infoOrCompleter match {
177182
case info: LazyType => info.completerTypeParams(tsym)
178183
case info => info.typeParams

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3190,7 +3190,7 @@ object Types {
31903190
def isTypeParam(implicit ctx: Context) = tl.paramNames.head.isTypeName
31913191
def paramName(implicit ctx: Context) = tl.paramNames(n)
31923192
def paramInfo(implicit ctx: Context) = tl.paramInfos(n)
3193-
def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = paramInfo.asSeenFrom(pre, pre.classSymbol)
3193+
def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = paramInfo
31943194
def paramInfoOrCompleter(implicit ctx: Context): Type = paramInfo
31953195
def paramVariance(implicit ctx: Context): Int = tl.paramNames(n).variance
31963196
def paramRef(implicit ctx: Context): Type = tl.paramRefs(n)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,9 +1238,10 @@ class Typer extends Namer
12381238
args = args.take(tparams.length)
12391239
}
12401240
def typedArg(arg: untpd.Tree, tparam: ParamInfo) = {
1241+
def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.normalizedPrefix)
12411242
val (desugaredArg, argPt) =
12421243
if (ctx.mode is Mode.Pattern)
1243-
(if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramInfo)
1244+
(if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds)
12441245
else
12451246
(arg, WildcardType)
12461247
if (tpt1.symbol.isClass)
@@ -1259,10 +1260,10 @@ class Typer extends Namer
12591260
// An unbounded `_` automatically adapts to type parameter bounds. This means:
12601261
// If we have wildcard application C[_], where `C` is a class replace
12611262
// with C[_ >: L <: H] where `L` and `H` are the bounds of the corresponding
1262-
// type parameter in `C`, avoiding any referemces to parameters of `C`.
1263-
// The transform does not apply for patters, where empty bounds translate to
1263+
// type parameter in `C`.
1264+
// The transform does not apply for patterns, where empty bounds translate to
12641265
// wildcard identifiers `_` instead.
1265-
res = res.withType(avoid(tparam.paramInfo, tpt1.tpe.typeParamSymbols))
1266+
res = res.withType(tparamBounds)
12661267
case _ =>
12671268
}
12681269
res

tests/pos/i4884.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
trait A
3+
trait TestConstructor1 { type F[_ <: A] }
4+
trait TestConstructor2[D] { type F[_ <: D] }
5+
6+
val v1: TestConstructor1 => Unit = { f =>
7+
type P[a <: A] = f.F[a] // OK
8+
}
9+
10+
val v2: TestConstructor2[A] => Unit = { f =>
11+
type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D
12+
}
13+
}

tests/run/TupleImpl.scala

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package test {
2+
3+
import annotation.showAsInfix
4+
5+
object typelevel {
6+
erased def erasedValue[T]: T = ???
7+
case class Typed[T](val value: T) { type Type = T }
8+
}
9+
10+
sealed trait Tuple
11+
object Empty extends Tuple
12+
13+
@showAsInfix
14+
case class *: [+H, +T <: Tuple](hd: H, tl: T) extends Tuple
15+
16+
object Tuple {
17+
import typelevel._
18+
type Empty = Empty.type
19+
20+
transparent def _cons[H, T <: Tuple] (x: H, xs: T): Tuple = new *:(x, xs)
21+
22+
transparent def _size(xs: Tuple): Int = xs match {
23+
case Empty => 0
24+
case _ *: xs1 => _size(xs1) + 1
25+
}
26+
27+
transparent def _index(xs: Tuple, n: Int): Any = xs match {
28+
case x *: _ if n == 0 => x
29+
case _ *: xs1 if n > 0 => _index(xs1, n - 1)
30+
}
31+
32+
class TupleOps(val xs: Tuple) extends AnyVal {
33+
34+
transparent def *: [H] (x: H): Tuple = new *:(x, xs)
35+
transparent def size: Int = _size(xs)
36+
37+
transparent def apply(n: Int): Any = {
38+
erased val typed = Typed(_index(xs, n))
39+
val result = _size(xs) match {
40+
case 1 =>
41+
n match {
42+
case 1 => xs.asInstanceOf[Tuple1[_]].__1
43+
}
44+
case 2 =>
45+
n match {
46+
case 1 => xs.asInstanceOf[Tuple2[_, _]].__1
47+
case 2 => xs.asInstanceOf[Tuple2[_, _]].__2
48+
}
49+
case 3 =>
50+
n match {
51+
case 1 => xs.asInstanceOf[Tuple3[_, _, _]].__1
52+
case 2 => xs.asInstanceOf[Tuple3[_, _, _]].__2
53+
case 3 => xs.asInstanceOf[Tuple3[_, _, _]].__3
54+
}
55+
case 4 =>
56+
n match {
57+
case 1 => xs.asInstanceOf[Tuple4[_, _, _, _]].__1
58+
case 2 => xs.asInstanceOf[Tuple4[_, _, _, _]].__2
59+
case 3 => xs.asInstanceOf[Tuple4[_, _, _, _]].__3
60+
case 4 => xs.asInstanceOf[Tuple4[_, _, _, _]].__4
61+
}
62+
}
63+
result.asInstanceOf[typed.Type]
64+
}
65+
transparent def **: (ys: Tuple): Tuple = ys match {
66+
case Empty => xs
67+
case y *: ys1 => y *: (ys1 **: xs)
68+
}
69+
transparent def head = xs match {
70+
case x *: _ => x
71+
}
72+
transparent def tail = xs match {
73+
case _ *: xs => xs
74+
}
75+
}
76+
77+
val emptyArray = Array[Object]()
78+
79+
transparent def toObj(t: Any) = t.asInstanceOf[Object]
80+
81+
transparent def toArray(t: Tuple): Array[Object] = t.size match {
82+
case 0 => emptyArray
83+
case 1 => Array(toObj(t(0)))
84+
case 2 => Array(toObj(t(0)), toObj(t(1)))
85+
case 3 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)))
86+
case 4 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)), toObj(t(3)))
87+
}
88+
89+
transparent implicit def tupleDeco(xs: Tuple): TupleOps = new TupleOps(xs)
90+
91+
transparent def apply(): Tuple = Empty
92+
transparent def apply(x1: Any): Tuple = x1 *: Empty
93+
transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty
94+
transparent def apply(x1: Any, x2: Any, x3: Any) = x1 *: x2 *: x3 *: Empty
95+
96+
val xs0 = Tuple()
97+
val xs1 = Tuple(2)
98+
val xs2 = Tuple(2, "a")
99+
val xs3 = Tuple(true, 1, 2.0)
100+
transparent val s0 = xs0.size; val s0c: 0 = s0
101+
transparent val s1 = xs1.size; val s1c: 1 = s1
102+
transparent val s2 = xs2.size; val s2c: 2 = s2
103+
transparent val s3 = xs3.size; val s3c: 3 = s3
104+
val e0 = xs3(0); val e0c: Boolean = e0
105+
val e1 = xs3(1); val e1c: Int = e1
106+
val e2 = xs3(2); val e2c: Double = e2
107+
108+
val conc0 = xs0 **: xs3
109+
val conc1 = xs3 **: xs0
110+
val conc2 = xs2 **: xs3
111+
val e3c: Int = conc0(1)
112+
val e4c: Int = conc1(1)
113+
val e5c: Int = conc2(0)
114+
val e6c: Double = conc2(4)
115+
116+
}
117+
118+
class Tuple1[+T1](val __1: T1) extends *:(__1, Empty)
119+
class Tuple2[+T1, +T2](val __1: T1, val __2: T2) extends *:(__1, *:(__2, Empty))
120+
class Tuple3[+T1, +T2, +T3](val __1: T1, val __2: T2, val __3: T3) extends *:(__1, *:(__2, *:(__3, Empty)))
121+
class Tuple4[+T1, +T2, +T3, +T4](val __1: T1, val __2: T2, val __3: T3, val __4: T4) extends *:(__1, *:(__2, *:(__3, *:(__4, Empty))))
122+
123+
}
124+
125+
object Test extends App

0 commit comments

Comments
 (0)