From 33d1d7cdeca123ab1f9e08a03801c40c703bc43b Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Sat, 4 Aug 2018 12:58:48 +0200
Subject: [PATCH] Fix #4884: asSeenFrom on incorrect prefix in type
applications
In this code we fetch the type parameter bounds from the definition, so we must
substitute their definition using `asSeenFrom` with the correct prefix.
The correct prefix is however hard to find: `TypeApplications.typeParams` finds
a `self: TypeRefs` (after however many steps), the correct prefix is
`self.prefix`.
In fact, a compositional typing rule would obtain the bounds from the type (or,
well, kind) of the type constructor; that would avoid the need for using
`asSeenFrom`. We only have a way to obtain such a kind for `NamedType` (where we
can take the `info`). However, that seems enough.
---
.../src/dotty/tools/dotc/typer/Checking.scala | 6 ++-
tests/pos/i4884.scala | 41 +++++++++++++++++++
2 files changed, 46 insertions(+), 1 deletion(-)
create mode 100644 tests/pos/i4884.scala
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
index ca2dd9b3c1dd..023617ef42d4 100644
--- a/compiler/src/dotty/tools/dotc/typer/Checking.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -65,10 +65,14 @@ object Checking {
* cannot handle those.
*/
def checkAppliedType(tree: AppliedTypeTree, boundsCheck: Boolean)(implicit ctx: Context) = {
+ def kind(tp: Type): Type = tp match {
+ case nTp: NamedType => nTp.info
+ case _ => tp
+ }
val AppliedTypeTree(tycon, args) = tree
// If `args` is a list of named arguments, return corresponding type parameters,
// otherwise return type parameters unchanged
- val tparams = tycon.tpe.typeParams
+ val tparams = kind(tycon.tpe).typeParams
def argNamed(tparam: ParamInfo) = args.find {
case NamedArg(name, _) => name == tparam.paramName
case _ => false
diff --git a/tests/pos/i4884.scala b/tests/pos/i4884.scala
new file mode 100644
index 000000000000..8b17dc05e022
--- /dev/null
+++ b/tests/pos/i4884.scala
@@ -0,0 +1,41 @@
+object Test {
+ trait A
+ trait TestConstructor1 { type F[_ <: A] }
+ trait TestConstructor2[D] {
+ type F[_ <: D]
+ class G[X <: D]
+ trait TestConstructor3[E] {
+ type G[_ <: D & E]
+ class H[X <: D & E]
+ }
+ }
+
+ val v1: TestConstructor1 => Unit = { f =>
+ type P[a <: A] = f.F[a] // OK
+ }
+
+ val v2: TestConstructor2[A] => Unit = { f =>
+ type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D
+ }
+
+ def f2(f: TestConstructor2[A]): Unit = {
+ type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D
+ }
+
+ // val v3: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = { f => g =>
+ // type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D
+ // // type Q[a <: A] = g.G[a]
+ // // type R[a <: A] = (f.F & g.G)[a]
+ // // type R[a <: A] = ([X] => f.F[X] & g.G[X])[a]
+ // }
+ def f3(f: TestConstructor2[A], g: f.TestConstructor3[A]): Unit = {
+ type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D
+ type Q[a <: A] = g.G[a]
+ // type R[a <: A] = (f.F & g.G)[a] // compiler error
+ type R[a <: A] = ([X <: A] => f.F[X] & g.G[X])[a]
+ type S[a <: A] = f.G[a] & g.H[a]
+ }
+ //val v4: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = {f => ???} // crash
+ //val v5: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = {(f: TestConstructor2[A]) => ???} // crash
+ //val v6: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = {(f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => ???} // crash
+}