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

Unsoundness: constructor params must not be in scope for the parent types #16270

Closed
sjrd opened this issue Nov 1, 2022 · 4 comments · Fixed by #16664
Closed

Unsoundness: constructor params must not be in scope for the parent types #16270

sjrd opened this issue Nov 1, 2022 · 4 comments · Fixed by #16664
Assignees
Labels
area:typer itype:bug itype:soundness Soundness bug (it lets us compile code that crashes at runtime with a ClassCastException)
Milestone

Comments

@sjrd
Copy link
Member

sjrd commented Nov 1, 2022

Summary

It should not be legal to reference constructor parameters in the supertypes of a class:

class Evil(val outer: Outer) extends Foo[outer.type](...)
class Evil(val outer: Outer) extends outer.Foo(...)

Either of those two things can be used to create unsoundness in the form of a ClassCastException, as shown below.

Fundamentally, the specification should reject those, as in either case, the linearization of Evil is ill-defined. Since the linearization is ill-defined, so is baseType, and everything falls down.

Note that Scala 2 rejects both variants, at least as far back as 2.10.9. @smarter's thesis calculus does not include constructor parameters in the context when typing parent types either.

Compiler version

Today's main, commit 7fd161f

Minimized code

First variant: outer in a type parameter

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) {
  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 // <-- Line 32 is here
    println(smuggled)
  }
}

Second variant: outer in the path of the superclass

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) {
  def doExtract(): Unit = extract(unpack)
}

// same object Test

Then run:

> scalac Reproduction.scala
> scala Test

Output

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at Test$.main(hello.scala:32)
        at Test.main(hello.scala)

Expectation

The compiler should reject either of those.

Scala 2 reports:

tests/run/hello.scala:42: error: not found: value outer
class Evil(val outer: Outer) extends Foo[outer.type](outer)
                                         ^

and

tests/run/hello.scala:43: error: not found: value outer
class Evil(val outer: Outer) extends outer.Foo
                                     ^

Credits

Collaboration with @shardulc and @smarter

@sjrd sjrd added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Nov 1, 2022
@smarter
Copy link
Member

smarter commented Nov 1, 2022

@b-studios you opened an issue related to this pattern (path-dependent type on a constructor parameter) a while ago: #5636, did you end up using it in some project? If so, heads up that it's probably going away due to the unsoundness problem reported in this issue :).

@smarter smarter self-assigned this Nov 1, 2022
@smarter smarter added area:typer itype:soundness Soundness bug (it lets us compile code that crashes at runtime with a ClassCastException) and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Nov 1, 2022
@b-studios
Copy link
Contributor

Hey, thanks :) Luckily we don't depend on it anymore. It was an experiment of embedding an effect system into Scala. Instead, we developed our own language ;)

@Kordyjan Kordyjan added this to the Future versions milestone Dec 12, 2022
@odersky
Copy link
Contributor

odersky commented Dec 14, 2022

@smarter @sjrd Is this being worked on? Since it is an unsoundness problem and the fix will make some code illegal, we should get this in sooner rather than later. I'll put it into the 3.3.0 milestone.

@odersky odersky modified the milestones: Future versions, 3.3.0-RC1 Dec 14, 2022
@smarter
Copy link
Member

smarter commented Dec 14, 2022

Ah I forgot about it but I'll try to get back to it before the milestone.

@Kordyjan Kordyjan modified the milestones: 3.3.0-RC1, 3.3.1-RC1 Jan 9, 2023
smarter added a commit to dotty-staging/dotty that referenced this issue Jan 11, 2023
smarter added a commit to dotty-staging/dotty that referenced this issue Jan 11, 2023
smarter added a commit to dotty-staging/dotty that referenced this issue Jan 11, 2023
smarter added a commit to dotty-staging/dotty that referenced this issue Jan 11, 2023
@sjrd sjrd modified the milestones: 3.3.1-RC1, 3.3.0-RC1 Jan 12, 2023
little-inferno pushed a commit to little-inferno/dotty that referenced this issue Jan 25, 2023
@Kordyjan Kordyjan modified the milestones: 3.3.0-RC1, 3.3.0 Aug 1, 2023
sjrd added a commit to sjrd/perspective that referenced this issue Aug 9, 2023
Scala 3.3.0 does not allow the `extends` clause of a class to
refer to constructor parameters. It was shown to be unsound.
See scala/scala3#16270

However, we can depend on paths that enclose the class definition.
So we introduce a container class `TreeMaps` that takes the `q: Q`
as parameter, and move the classes that extend `q.reflect.TreeMap`
inside that container.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:typer itype:bug itype:soundness Soundness bug (it lets us compile code that crashes at runtime with a ClassCastException)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants