Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow constructor params from appearing in parent types for soundness #16664

Merged
merged 1 commit into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
dwijnand marked this conversation as resolved.
Show resolved Hide resolved
// 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.