-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #4884: Fix handling of bounds of type lambdas #4902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0a280b3
3739026
deecd9f
210c6ed
3eb6d92
900982c
e25e286
3f639b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1238,9 +1238,10 @@ class Typer extends Namer | |
args = args.take(tparams.length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test failure seems genuinely to appear with this new commit (not with my changes) — everything works with the parent.
EDIT: cut stacktrace. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fixed in the latest commit. |
||
} | ||
def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { | ||
def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty))) | ||
val (desugaredArg, argPt) = | ||
if (ctx.mode is Mode.Pattern) | ||
(if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramInfo) | ||
(if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds) | ||
else | ||
(arg, WildcardType) | ||
if (tpt1.symbol.isClass) | ||
|
@@ -1259,10 +1260,10 @@ class Typer extends Namer | |
// An unbounded `_` automatically adapts to type parameter bounds. This means: | ||
// If we have wildcard application C[_], where `C` is a class replace | ||
// with C[_ >: L <: H] where `L` and `H` are the bounds of the corresponding | ||
// type parameter in `C`, avoiding any referemces to parameters of `C`. | ||
// The transform does not apply for patters, where empty bounds translate to | ||
// type parameter in `C`. | ||
// The transform does not apply for patterns, where empty bounds translate to | ||
// wildcard identifiers `_` instead. | ||
res = res.withType(avoid(tparam.paramInfo, tpt1.tpe.typeParamSymbols)) | ||
res = res.withType(tparamBounds) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, avoiding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I believe that's correct. |
||
case _ => | ||
} | ||
res | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
object Test { | ||
trait A | ||
trait B | ||
trait TestConstructor1 { type F[X <: A] <: TestConstructor2[A] } | ||
trait TestConstructor2[D] { | ||
type F[_ <: D] | ||
class G[X <: D] | ||
trait TestConstructor3[E] { | ||
type G[X <: D & E] <: TestConstructor2.this.F[X] & H[X] | ||
class H[X <: D & E] { | ||
type A <: G[X] | ||
} | ||
} | ||
} | ||
trait TestConstructor4[D] { | ||
trait TestConstructor5[E] { | ||
trait MSetLike[X <: D & E, This <: MSet[X] with MSetLike[X, This]] | ||
trait MSet[X <: D & E] extends MSetLike[X, MSet[X]] | ||
} | ||
} | ||
|
||
val v1: TestConstructor1 => Unit = { f => | ||
type P[a <: A] = f.F[a] | ||
} | ||
|
||
val v2: TestConstructor2[A] => Unit = { f => | ||
type P[a <: A] = f.F[a] | ||
} | ||
|
||
def f2(f: TestConstructor2[A]): Unit = { | ||
type P[a <: A] = f.F[a] | ||
} | ||
|
||
type C = A & B | ||
def f3(f: TestConstructor2[A], g: f.TestConstructor3[B]): Unit = { | ||
type P1[a <: A] = f.F[a] | ||
type P2[a <: A] = f.G[a] | ||
type Q1[c <: C] = g.G[c] | ||
type Q2[c <: C] = g.H[c] | ||
type R1[c <: C] = f.G[c] & g.H[c] | ||
type R2[c <: C] = f.G[c] | g.H[c] | ||
type S1[c <: C] = ([X <: C] => f.F[X] & g.G[X])[c] | ||
type S2[c <: C] = ([X <: C] => f.F[X] | g.G[X])[c] | ||
} | ||
def f3(f: TestConstructor4[A], g: f.TestConstructor5[B]): Unit = { | ||
type P1[c <: C] = g.MSet[c] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,81 @@ | ||
import annotation.showAsInfix | ||
|
||
object typelevel { | ||
erased def erasedValue[T]: T = ??? | ||
class Typed[T](val value: T) { type Type = T } | ||
} | ||
|
||
sealed trait Tuple | ||
object Empty extends Tuple | ||
|
||
object Tuple { | ||
object Empty extends Tuple | ||
@showAsInfix | ||
final case class *: [H, T <: Tuple](hd: H, tl: T) extends Tuple | ||
|
||
object Tuple { | ||
import typelevel._ | ||
type Empty = Empty.type | ||
|
||
@showAsInfix | ||
final case class *: [H, T <: Tuple](hd: H, tl: T) extends Tuple | ||
class TupleOps(val xs: Tuple) extends AnyVal { | ||
transparent def *: [H] (x: H): Tuple = new *:(x, xs) | ||
transparent def size: Int = xs match { | ||
case Empty => 0 | ||
case _ *: xs1 => xs1.size + 1 | ||
} | ||
transparent def apply(n: Int): Any = xs match { | ||
case x *: _ if n == 0 => x | ||
case _ *: xs1 if n > 0 => xs1.apply(n - 1) | ||
} | ||
transparent def **: (ys: Tuple): Tuple = ys match { | ||
case Empty => xs | ||
case y *: ys1 => y *: (ys1 **: xs) | ||
} | ||
transparent def head = xs match { | ||
case x *: _ => x | ||
} | ||
transparent def tail = xs match { | ||
case _ *: xs => xs | ||
} | ||
} | ||
|
||
class HListDeco(val xs: Tuple) extends AnyVal { | ||
transparent def *: [H] (x: H): Tuple = Tuple.*:.apply(x, xs) | ||
val emptyArray = Array[Object]() | ||
|
||
transparent def size: Int = Tuple.size(xs) | ||
} | ||
transparent def toObj(t: Any) = t.asInstanceOf[Object] | ||
|
||
transparent def size(xs: Tuple): Int = xs match { | ||
case Empty => 0 | ||
case _ *: xs1 => size(xs1) + 1 | ||
transparent def toArray(t: Tuple): Array[Object] = t.size match { | ||
case 0 => emptyArray | ||
case 1 => Array(toObj(t(0))) | ||
case 2 => Array(toObj(t(0)), toObj(t(1))) | ||
case 3 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2))) | ||
case 4 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)), toObj(t(3))) | ||
} | ||
|
||
transparent implicit def hlistDeco(xs: Tuple): HListDeco = new HListDeco(xs) | ||
transparent implicit def tupleDeco(xs: Tuple): TupleOps = new TupleOps(xs) | ||
|
||
transparent def apply(): Tuple = Empty | ||
transparent def apply(x1: Any): Tuple = x1 *: Empty | ||
transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty | ||
transparent def apply(x1: Any, x2: Any, x3: Any) = x1 *: x2 *: x3 *: Empty | ||
|
||
val xs0 = Tuple() | ||
val xs1 = Tuple(2) | ||
val xs2 = Tuple(2, "a") | ||
val s0 = xs0.size | ||
val s1 = xs1.size | ||
val s2 = xs2.size | ||
val xs3 = Tuple(true, 1, 2.0) | ||
transparent val s0 = xs0.size; val s0c: 0 = s0 | ||
transparent val s1 = xs1.size; val s1c: 1 = s1 | ||
transparent val s2 = xs2.size; val s2c: 2 = s2 | ||
transparent val s3 = xs3.size; val s3c: 3 = s3 | ||
val e0 = xs3(0); val e0c: Boolean = e0 | ||
val e1 = xs3(1); val e1c: Int = e1 | ||
val e2 = xs3(2); val e2c: Double = e2 | ||
|
||
val conc0 = xs0 **: xs3 | ||
val conc1 = xs3 **: xs0 | ||
val conc2 = xs2 **: xs3 | ||
val e3c: Int = conc0(1) | ||
val e4c: Int = conc1(1) | ||
val e5c: Int = conc2(0) | ||
val e6c: Double = conc2(4) | ||
|
||
} | ||
|
||
object Test extends App |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package test { | ||
|
||
import annotation.showAsInfix | ||
|
||
object typelevel { | ||
erased def erasedValue[T]: T = ??? | ||
case class Typed[T](val value: T) { type Type = T } | ||
} | ||
|
||
sealed trait Tuple | ||
object Empty extends Tuple | ||
|
||
@showAsInfix | ||
case class *: [+H, +T <: Tuple](hd: H, tl: T) extends Tuple | ||
|
||
object Tuple { | ||
import typelevel._ | ||
type Empty = Empty.type | ||
|
||
transparent def _cons[H, T <: Tuple] (x: H, xs: T): Tuple = new *:(x, xs) | ||
|
||
transparent def _size(xs: Tuple): Int = xs match { | ||
case Empty => 0 | ||
case _ *: xs1 => _size(xs1) + 1 | ||
} | ||
|
||
transparent def _index(xs: Tuple, n: Int): Any = xs match { | ||
case x *: _ if n == 0 => x | ||
case _ *: xs1 if n > 0 => _index(xs1, n - 1) | ||
} | ||
|
||
class TupleOps(val xs: Tuple) extends AnyVal { | ||
|
||
transparent def *: [H] (x: H): Tuple = new *:(x, xs) | ||
transparent def size: Int = _size(xs) | ||
|
||
transparent def apply(n: Int): Any = { | ||
erased val typed = Typed(_index(xs, n)) | ||
val result = _size(xs) match { | ||
case 1 => | ||
n match { | ||
case 1 => xs.asInstanceOf[Tuple1[_]].__1 | ||
} | ||
case 2 => | ||
n match { | ||
case 1 => xs.asInstanceOf[Tuple2[_, _]].__1 | ||
case 2 => xs.asInstanceOf[Tuple2[_, _]].__2 | ||
} | ||
case 3 => | ||
n match { | ||
case 1 => xs.asInstanceOf[Tuple3[_, _, _]].__1 | ||
case 2 => xs.asInstanceOf[Tuple3[_, _, _]].__2 | ||
case 3 => xs.asInstanceOf[Tuple3[_, _, _]].__3 | ||
} | ||
case 4 => | ||
n match { | ||
case 1 => xs.asInstanceOf[Tuple4[_, _, _, _]].__1 | ||
case 2 => xs.asInstanceOf[Tuple4[_, _, _, _]].__2 | ||
case 3 => xs.asInstanceOf[Tuple4[_, _, _, _]].__3 | ||
case 4 => xs.asInstanceOf[Tuple4[_, _, _, _]].__4 | ||
} | ||
} | ||
result.asInstanceOf[typed.Type] | ||
} | ||
transparent def **: (ys: Tuple): Tuple = ys match { | ||
case Empty => xs | ||
case y *: ys1 => y *: (ys1 **: xs) | ||
} | ||
transparent def head = xs match { | ||
case x *: _ => x | ||
} | ||
transparent def tail = xs match { | ||
case _ *: xs => xs | ||
} | ||
} | ||
|
||
val emptyArray = Array[Object]() | ||
|
||
transparent def toObj(t: Any) = t.asInstanceOf[Object] | ||
|
||
transparent def toArray(t: Tuple): Array[Object] = t.size match { | ||
case 0 => emptyArray | ||
case 1 => Array(toObj(t(0))) | ||
case 2 => Array(toObj(t(0)), toObj(t(1))) | ||
case 3 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2))) | ||
case 4 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)), toObj(t(3))) | ||
} | ||
|
||
transparent implicit def tupleDeco(xs: Tuple): TupleOps = new TupleOps(xs) | ||
|
||
transparent def apply(): Tuple = Empty | ||
transparent def apply(x1: Any): Tuple = x1 *: Empty | ||
transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty | ||
transparent def apply(x1: Any, x2: Any, x3: Any) = x1 *: x2 *: x3 *: Empty | ||
|
||
val xs0 = Tuple() | ||
val xs1 = Tuple(2) | ||
val xs2 = Tuple(2, "a") | ||
val xs3 = Tuple(true, 1, 2.0) | ||
transparent val s0 = xs0.size; val s0c: 0 = s0 | ||
transparent val s1 = xs1.size; val s1c: 1 = s1 | ||
transparent val s2 = xs2.size; val s2c: 2 = s2 | ||
transparent val s3 = xs3.size; val s3c: 3 = s3 | ||
val e0 = xs3(0); val e0c: Boolean = e0 | ||
val e1 = xs3(1); val e1c: Int = e1 | ||
val e2 = xs3(2); val e2c: Double = e2 | ||
|
||
val conc0 = xs0 **: xs3 | ||
val conc1 = xs3 **: xs0 | ||
val conc2 = xs2 **: xs3 | ||
val e3c: Int = conc0(1) | ||
val e4c: Int = conc1(1) | ||
val e5c: Int = conc2(0) | ||
val e6c: Double = conc2(4) | ||
|
||
} | ||
|
||
class Tuple1[+T1](val __1: T1) extends *:(__1, Empty) | ||
class Tuple2[+T1, +T2](val __1: T1, val __2: T2) extends *:(__1, *:(__2, Empty)) | ||
class Tuple3[+T1, +T2, +T3](val __1: T1, val __2: T2, val __3: T3) extends *:(__1, *:(__2, *:(__3, Empty))) | ||
class Tuple4[+T1, +T2, +T3, +T4](val __1: T1, val __2: T2, val __3: T3, val __4: T4) extends *:(__1, *:(__2, *:(__3, *:(__4, Empty)))) | ||
|
||
} | ||
|
||
object Test extends App |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we further refine
isTrivial
to cover the case whenprefix
is static?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure we want to do this. The purpose of
isTrivial
is to fall back to a special case to avoid cyclic reference errors. It seems we don't need to include staticthis
types to achieve this. On the other hand, theisStatic
test could prompt cyclic references itself.