Skip to content

Commit 2833b36

Browse files
authored
Disallow constructor params from appearing in parent types for soundness (#16664)
Fixes #16270.
2 parents ed81385 + 56018ac commit 2833b36

File tree

9 files changed

+63
-18
lines changed

9 files changed

+63
-18
lines changed

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

+8
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
390390
case impl: Template =>
391391
for parent <- impl.parents do
392392
Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos)
393+
// Constructor parameters are in scope when typing a parent.
394+
// While they can safely appear in a parent tree, to preserve
395+
// soundness we need to ensure they don't appear in a parent
396+
// type (#16270).
397+
val illegalRefs = parent.tpe.namedPartsWith(p => p.symbol.is(ParamAccessor) && (p.symbol.owner eq sym))
398+
if illegalRefs.nonEmpty then
399+
report.error(
400+
em"The type of a class parent cannot refer to constructor parameters, but ${parent.tpe} refers to ${illegalRefs.map(_.name.show).mkString(",")}", parent.srcPos)
393401
// Add SourceFile annotation to top-level classes
394402
if sym.owner.is(Package) then
395403
if ctx.compilationUnit.source.exists && sym != defn.SourceFileAnnot then

tests/init/neg/early-promote4.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ class Outer {
88
trait B {
99
def bar() = assert(a == 5)
1010
}
11-
}
1211

13-
class M(val o: Outer) extends A with o.B {
14-
val n: Int = 10
12+
class M extends A with B {
13+
val n: Int = 10
14+
}
1515
}
1616

1717
class Dummy {
1818
val m: Int = n + 4
1919
val n: Int = 10 // error
20-
}
20+
}

tests/init/neg/early-promote5.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ class Outer {
88
trait B {
99
def bar(x: A) = println(a)
1010
}
11-
}
1211

13-
class M(val o: Outer, c: Container) extends A with o.B
12+
class M(c: Container) extends A with B
13+
}
1414

1515
class Container {
1616
val o = new Outer
17-
val m = new M(o, this) // error
17+
val m = new o.M(this) // error
1818
val s = "hello"
1919
}
2020

tests/neg/i16270a.scala

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class Outer {
2+
type Smuggler
3+
var smuggler: Option[Smuggler] = None
4+
}
5+
class Foo[T](var unpack: T)
6+
class Evil(val outer: Outer, extract: outer.type => Unit) extends Foo[outer.type](outer) { // error
7+
def doExtract(): Unit = extract(unpack)
8+
}
9+
10+
object Test {
11+
def main(args: Array[String]): Unit = {
12+
val outer1 = new Outer { type Smuggler = Int }
13+
outer1.smuggler = Some(5)
14+
val evil1 = new Evil(outer1, _ => ())
15+
16+
val outer2 = new Outer { type Smuggler = String }
17+
var extractedOuter2: Option[outer2.type] = None
18+
val evil2 = new Evil(outer2, x => extractedOuter2 = Some(x))
19+
20+
evil2.unpack = evil1.unpack
21+
evil2.doExtract()
22+
val smuggled: String = extractedOuter2.get.smuggler.get
23+
println(smuggled)
24+
}
25+
}

tests/neg/i16270b.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Outer {
2+
class Foo(var unpack: Outer.this.type)
3+
4+
type Smuggler
5+
var smuggler: Option[Smuggler] = None
6+
}
7+
class Evil(val outer: Outer, extract: outer.type => Unit) extends outer.Foo(outer) { // error
8+
def doExtract(): Unit = extract(unpack)
9+
}

tests/neg/i16270c.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo[T <: Singleton](x: T)
2+
class Outer
3+
class Evil(val outer: Outer) extends Foo(outer) // error (because outer.type appears in the inferred type)

tests/neg/i3935.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum Foo3[T](x: T) {
2+
case Bar[S, T](y: T) extends Foo3[y.type](y) // error
3+
}
4+
5+
// val foo: Foo3.Bar[Nothing, 3] = Foo3.Bar(3)
6+
// val bar = foo
7+
8+
// def baz[T](f: Foo3[T]): f.type = f
9+
10+
// val qux = baz(bar) // existentials are back in Dotty?

tests/pos/i5636.scala renamed to tests/neg/i5636.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ trait Bar[X] {
44
def foo: X = ???
55
}
66
// same for `class Foo(...)...`
7-
trait Foo(val a: A) extends Bar[a.type] {
7+
trait Foo(val a: A) extends Bar[a.type] { // error
88
val same: a.type = foo
99
}

tests/pos/i3935.scala

-10
This file was deleted.

0 commit comments

Comments
 (0)