Skip to content

Commit e8b05e2

Browse files
committed
Fix #4031: Check arguments of dependent methods for realizability
Also some additions to realizability tests
1 parent 2476cb4 commit e8b05e2

File tree

7 files changed

+88
-5
lines changed

7 files changed

+88
-5
lines changed

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
205205
Checking.checkInstantiable(tree.tpe, nu.pos)
206206
withNoCheckNews(nu :: Nil)(super.transform(tree))
207207
case _ =>
208+
tree.fun.tpe.widen match {
209+
case mt: MethodType if mt.isDependent || mt.isParamDependent =>
210+
Checking.checkRealizableArgs(mt, tree.args)
211+
case _ =>
212+
}
208213
super.transform(tree)
209214
}
210215
case tree: TypeApply =>

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,18 @@ object Checking {
151151
ctx.errorOrMigrationWarning(em"$tp is not a legal path\nsince it${rstatus.msg}", pos)
152152
}
153153

154+
/** Check that all arguments to dependent parameters are realizable */
155+
def checkRealizableArgs(mt: MethodType, args: List[Tree])(implicit ctx: Context): Unit = {
156+
val paramTypes = mt.paramInfos.toArray
157+
val resultType = mt.resultType
158+
def isDependent(paramRef: ParamRef, i: Int) =
159+
(i + 1 until paramTypes.length).exists(j => paramRef.occursIn(paramTypes(j))) ||
160+
paramRef.occursIn(resultType)
161+
for ((paramRef, i) <- mt.paramRefs.zipWithIndex)
162+
if (isDependent(paramRef, i))
163+
checkRealizable(args(i).tpe, args(i).pos)
164+
}
165+
154166
/** A type map which checks that the only cycles in a type are F-bounds
155167
* and that protects all F-bounded references by LazyRefs.
156168
*/

tests/neg/i4031.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
object App {
2+
trait A { type L >: Any}
3+
def upcast(a: A, x: Any): a.L = x
4+
lazy val p: A { type L <: Nothing } = p
5+
val q = new A { type L = Any }
6+
def coerce(x: Any): Int = upcast(p, x) // error: not a legal path
7+
def coerce1(x: Any): Any = upcast(q, x) // ok
8+
def coerce2(x: Any): Int = upcast(p, x): p.L // error: not a legal path // error
9+
10+
def compare(x: A, y: x.L) = assert(x == y)
11+
12+
def compare2(x: A)(y: x.type) = assert(x == y)
13+
14+
15+
def main(args: Array[String]): Unit = {
16+
//println(coerce("Uh oh!"))
17+
compare(q, q) // OK
18+
compare(p, p) // error: not a legal path
19+
compare2(p)(p) // error: not a legal path
20+
}
21+
}

tests/neg/i50-volatile.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@ class Test {
1212

1313
class Client extends o.Inner // old-error // old-error
1414

15-
def xToString(x: o.X): String = x // old-error
15+
def xToString(x: o.X): String = x // error
1616

1717
def intToString(i: Int): String = xToString(i)
1818
}
19-
object Test2 {
2019

21-
import Test.o._ // error
20+
object Test2 {
21+
trait A {
22+
type X = String
23+
}
24+
trait B {
25+
type X = Int
26+
}
27+
lazy val o: A & B = ???
2228

23-
def xToString(x: X): String = x
29+
def xToString(x: o.X): String = x // error
2430

31+
def intToString(i: Int): String = xToString(i)
2532
}

tests/neg/realizability.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class C { type T }
2+
3+
class Test {
4+
5+
type D <: C
6+
7+
lazy val a: C = ???
8+
final lazy val b: C = ???
9+
val c: D = ???
10+
final lazy val d: D = ???
11+
12+
val x1: a.T = ??? // error: not a legal path, since a is lazy & non-final
13+
val x2: b.T = ??? // OK, b is lazy but concrete
14+
val x3: c.T = ??? // OK, c is abstract but strict
15+
val x4: d.T = ??? // error: not a legal path since d is abstract and lazy
16+
17+
val y1: Singleton = a
18+
val y2: Singleton = a
19+
val y3: Singleton = a
20+
val y4: Singleton = a
21+
22+
}

tests/neg/z1720.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package test
2+
3+
class Thing {
4+
def info: Info[this.type] = InfoRepository.getInfo(this)
5+
def info2: Info[this.type] = {
6+
def self: this.type = this
7+
InfoRepository.getInfo(self) // error: not a legal path
8+
}
9+
}
10+
11+
trait Info[T]
12+
case class InfoImpl[T](thing: T) extends Info[T]
13+
14+
object InfoRepository {
15+
def getInfo(t: Thing): Info[t.type] = InfoImpl(t)
16+
}

tests/pos/z1720.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package test
33
class Thing {
44
def info: Info[this.type] = InfoRepository.getInfo(this)
55
def info2: Info[this.type] = {
6-
def self: this.type = this
6+
val self: this.type = this
77
InfoRepository.getInfo(self)
88
}
99
}

0 commit comments

Comments
 (0)