Skip to content

Commit

Permalink
Disallow constructor params from appearing in parent types for soundn…
Browse files Browse the repository at this point in the history
…ess (#16664)

Fixes #16270.
  • Loading branch information
sjrd authored Jan 12, 2023
2 parents ed81385 + 56018ac commit 2833b36
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 18 deletions.
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
case impl: Template =>
for parent <- impl.parents do
Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos)
// Constructor parameters are in scope when typing a parent.
// While they can safely appear in a parent tree, to preserve
// soundness we need to ensure they don't appear in a parent
// type (#16270).
val illegalRefs = parent.tpe.namedPartsWith(p => p.symbol.is(ParamAccessor) && (p.symbol.owner eq sym))
if illegalRefs.nonEmpty then
report.error(
em"The type of a class parent cannot refer to constructor parameters, but ${parent.tpe} refers to ${illegalRefs.map(_.name.show).mkString(",")}", parent.srcPos)
// Add SourceFile annotation to top-level classes
if sym.owner.is(Package) then
if ctx.compilationUnit.source.exists && sym != defn.SourceFileAnnot then
Expand Down
8 changes: 4 additions & 4 deletions tests/init/neg/early-promote4.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ class Outer {
trait B {
def bar() = assert(a == 5)
}
}

class M(val o: Outer) extends A with o.B {
val n: Int = 10
class M extends A with B {
val n: Int = 10
}
}

class Dummy {
val m: Int = n + 4
val n: Int = 10 // error
}
}
6 changes: 3 additions & 3 deletions tests/init/neg/early-promote5.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ class Outer {
trait B {
def bar(x: A) = println(a)
}
}

class M(val o: Outer, c: Container) extends A with o.B
class M(c: Container) extends A with B
}

class Container {
val o = new Outer
val m = new M(o, this) // error
val m = new o.M(this) // error
val s = "hello"
}

Expand Down
25 changes: 25 additions & 0 deletions tests/neg/i16270a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Outer {
type Smuggler
var smuggler: Option[Smuggler] = None
}
class Foo[T](var unpack: T)
class Evil(val outer: Outer, extract: outer.type => Unit) extends Foo[outer.type](outer) { // error
def doExtract(): Unit = extract(unpack)
}

object Test {
def main(args: Array[String]): Unit = {
val outer1 = new Outer { type Smuggler = Int }
outer1.smuggler = Some(5)
val evil1 = new Evil(outer1, _ => ())

val outer2 = new Outer { type Smuggler = String }
var extractedOuter2: Option[outer2.type] = None
val evil2 = new Evil(outer2, x => extractedOuter2 = Some(x))

evil2.unpack = evil1.unpack
evil2.doExtract()
val smuggled: String = extractedOuter2.get.smuggler.get
println(smuggled)
}
}
9 changes: 9 additions & 0 deletions tests/neg/i16270b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Outer {
class Foo(var unpack: Outer.this.type)

type Smuggler
var smuggler: Option[Smuggler] = None
}
class Evil(val outer: Outer, extract: outer.type => Unit) extends outer.Foo(outer) { // error
def doExtract(): Unit = extract(unpack)
}
3 changes: 3 additions & 0 deletions tests/neg/i16270c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo[T <: Singleton](x: T)
class Outer
class Evil(val outer: Outer) extends Foo(outer) // error (because outer.type appears in the inferred type)
10 changes: 10 additions & 0 deletions tests/neg/i3935.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
enum Foo3[T](x: T) {
case Bar[S, T](y: T) extends Foo3[y.type](y) // error
}

// val foo: Foo3.Bar[Nothing, 3] = Foo3.Bar(3)
// val bar = foo

// def baz[T](f: Foo3[T]): f.type = f

// val qux = baz(bar) // existentials are back in Dotty?
2 changes: 1 addition & 1 deletion tests/pos/i5636.scala → tests/neg/i5636.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ trait Bar[X] {
def foo: X = ???
}
// same for `class Foo(...)...`
trait Foo(val a: A) extends Bar[a.type] {
trait Foo(val a: A) extends Bar[a.type] { // error
val same: a.type = foo
}
10 changes: 0 additions & 10 deletions tests/pos/i3935.scala

This file was deleted.

0 comments on commit 2833b36

Please sign in to comment.