Skip to content

Commit 16ca40f

Browse files
committed
Fix #5854: check type annotations for soundness
1 parent fe38137 commit 16ca40f

File tree

5 files changed

+133
-15
lines changed

5 files changed

+133
-15
lines changed

compiler/src/dotty/tools/dotc/transform/init/Summarization.scala

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ object Summarization {
107107

108108
case Typed(expr, tpt) =>
109109
if (tpt.tpe.hasAnnotation(defn.UncheckedAnnot)) Summary.empty
110-
else analyze(expr)
110+
else analyze(expr) ++ effectsOfType(tpt.tpe, tpt)
111111

112112
case NamedArg(name, arg) =>
113113
analyze(arg)
@@ -201,20 +201,7 @@ object Summarization {
201201

202202
case tdef: TypeDef =>
203203
if tdef.isClassDef then Summary.empty
204-
else {
205-
var summary = Summary.empty
206-
val tp = tdef.symbol.info
207-
val traverser = new TypeTraverser {
208-
def traverse(tp: Type): Unit = tp match {
209-
case TermRef(_: SingletonType, _) =>
210-
summary = summary + analyze(tp, tdef.rhs)
211-
case _ =>
212-
traverseChildren(tp)
213-
}
214-
}
215-
traverser.traverse(tp)
216-
summary
217-
}
204+
else Summary(effectsOfType(tdef.symbol.info, tdef.rhs))
218205

219206
case _: Import | _: Export =>
220207
Summary.empty
@@ -227,6 +214,19 @@ object Summarization {
227214
else summary
228215
}
229216

217+
private def effectsOfType(tp: Type, source: Tree)(implicit env: Env): Effects =
218+
var summary = Summary.empty
219+
val traverser = new TypeTraverser {
220+
def traverse(tp: Type): Unit = tp match {
221+
case TermRef(_: SingletonType, _) =>
222+
summary = summary + analyze(tp, source)
223+
case _ =>
224+
traverseChildren(tp)
225+
}
226+
}
227+
traverser.traverse(tp)
228+
summary.effs
229+
230230
def analyze(tp: Type, source: Tree)(implicit env: Env): Summary =
231231
trace("summarizing " + tp.show, init, s => s.asInstanceOf[Summary].show) {
232232
val summary: Summary = tp match {

tests/init/neg/i5854.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class B {
2+
val a: String = (((1: Any): b.A): Nothing): String
3+
val b: { type A >: Any <: Nothing } = loop() // error
4+
def loop(): Nothing = loop()
5+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// A contains a field A.theValue$$local accessible using the
2+
// generated getter A.theValue()
3+
class A(val theValue: Int) {
4+
val theValueInA = theValue // use the constructor parameter theValue
5+
6+
def getTheValue = theValue // virtual call to the getter theValue()
7+
}
8+
9+
// B contains a field B.theValue$$local accessible using the getter
10+
// B.theValue() which overrides A.theValue()
11+
class B(override val theValue: Int) extends A(42) {
12+
val theValueInB = theValue
13+
}
14+
15+
// Bz contains a field Bz.theValue$$local accessible using the getter
16+
// Bz.theValue() which overrides A.theValue()
17+
class Bz extends A(42) {
18+
override val theValue: Int = 10
19+
val theValueInBz = theValue
20+
}
21+
22+
// C does not contain a field C.theValue$$local, it contains
23+
// a getter C.theValue() which only calls super.theValue()
24+
class C(override val theValue: Int) extends A(theValue)
25+
26+
// D contains a field D.other$$local and a corresponding getter.
27+
class D(val other: Int) extends A(other)
28+
29+
30+
// NonVal does not contain a field NonVal.theValue$$local.
31+
class NonVal(theValue: Int) extends A(theValue) {
32+
def getTheValueInNonVal = theValue // use the constructor parameter theValue
33+
}
34+
35+
// X contains a field X.theValue$$local accessible using the getter
36+
// X.theValue() which overrides A.theValue()
37+
class X(override val theValue: Int) extends NonVal(0)
38+
39+
// Y does not contain a field Y.theValue$$local, it contains
40+
// a getter Y.theValue() which only calls super.theValue()
41+
class Y(override val theValue: Int) extends NonVal(theValue)
42+
43+
44+
object Test {
45+
def printFields(obj: Any) =
46+
println(obj.getClass.getDeclaredFields.map(_.toString).sorted.toList.mkString("\n"))
47+
48+
def main(args: Array[String]): Unit = {
49+
val b10 = new B(10)
50+
val bz = new Bz
51+
val c11 = new C(11)
52+
val d12 = new D(12)
53+
val nv13 = new NonVal(13)
54+
val x14 = new X(14)
55+
val y15 = new Y(15)
56+
57+
println("B:")
58+
printFields(b10)
59+
println("Bz:")
60+
printFields(bz)
61+
println("C:")
62+
printFields(c11)
63+
println("D:")
64+
printFields(d12)
65+
println("NonVal:")
66+
printFields(nv13)
67+
println("X:")
68+
printFields(x14)
69+
println("Y:")
70+
printFields(y15)
71+
72+
73+
assert(b10.getTheValue == 10)
74+
assert(b10.theValue == 10)
75+
assert(b10.theValueInB == 10)
76+
assert(b10.theValueInA == 42)
77+
78+
assert(bz.getTheValue == 10)
79+
assert(bz.theValue == 10)
80+
assert(bz.theValueInBz == 10)
81+
assert(bz.theValueInA == 42)
82+
83+
84+
assert(x14.theValue == 14)
85+
assert(x14.getTheValue == 14)
86+
assert(x14.getTheValueInNonVal == 0)
87+
assert(x14.theValueInA == 0)
88+
}
89+
}

tests/init/neg/sep-child-parent.check

Whitespace-only changes.

tests/neg-strict/i5854.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
object bar {
2+
trait Sub {
3+
type M
4+
type L <: M
5+
type U >: M
6+
type M2 >: L <: U
7+
}
8+
class Box[V](val v: V)
9+
10+
class Caster[LL, UU] {
11+
trait S2 {
12+
type L = LL
13+
type U = UU
14+
}
15+
final lazy val nothing: Nothing = nothing
16+
final lazy val sub: S2 & Sub = nothing
17+
final lazy val box : Box[S2 & Sub] = new Box(nothing)
18+
def upcast(t: box.v.M2): box.v.M2 = t // error // error
19+
}
20+
def main(args : Array[String]) : Unit = {
21+
val zero : String = (new Caster[Int,String]()).upcast(0)
22+
println("...")
23+
}
24+
}

0 commit comments

Comments
 (0)