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

Good old unsoundness, CCE #4031

Closed
hobwekiva opened this issue Feb 21, 2018 · 8 comments · Fixed by #4039 or #5568
Closed

Good old unsoundness, CCE #4031

hobwekiva opened this issue Feb 21, 2018 · 8 comments · Fixed by #4039 or #5568

Comments

@hobwekiva
Copy link

hobwekiva commented Feb 21, 2018

http://io.livecode.ch/learn/namin/unsound/scala

object App {
  trait A { type L >: Any}
  def upcast(a: A, x: Any): a.L = x
  lazy val p: A { type L <: Nothing } = p
  def coerce(x: Any): Int = upcast(p, x)

  def main(args: Array[String]): Unit = {
    println(coerce("Uh oh!"))
  }
}

lazy above is not necessary

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
	at App$.coerce(HelloWorld.scala:5)
	at App$.main(HelloWorld.scala:8)
	at App.main(HelloWorld.scala)
@Blaisorblade
Copy link
Contributor

Without lazy you need null, right? Because otherwise you can't construct a value with p's type.

This looks related to #50 — in this example, it seems that what should be illegal is allowing dependent method upcast to be applied to non-stable argument p.

(If one really wanted it could be legal to allow upcast(p, x) with return type Any (the upper bound of a.L), in the spirit of typing rule (Tapp) in http://drops.dagstuhl.de/opus/volltexte/2017/7276/, but I'm not sure Scala intends to allow this).

@odersky
Copy link
Contributor

odersky commented Feb 26, 2018

I believe the lazy val

lazy val p: A { type L <: Nothing } = p

should not be realizable. We miss a check here.

odersky added a commit to dotty-staging/dotty that referenced this issue Feb 26, 2018
@Blaisorblade
Copy link
Contributor

Talked with Martin a lot about this and #4036. Turns out Dotty already switched to the refined logic — p is legal but unstable, so applying dependent methods/functions to it requires care.

(If one really wanted it could be legal to allow upcast(p, x) with return type Any (the upper bound of a.L), in the spirit of typing rule (Tapp) in http://drops.dagstuhl.de/opus/volltexte/2017/7276/, but I'm not sure Scala intends to allow this).

There is indeed some logic for it. If method f is dependent and argument e is unstable, f(e) could be rewritten in ANF, and typer produced the same result (a "more general" type). But typer can only approximate whether e is stable (PostTyper is more precise), yet the types it produces can't be refined later. We'll need to resume thinking about this.

odersky added a commit to dotty-staging/dotty that referenced this issue Feb 26, 2018
    isDependent -> isResultDependent

so that `isDependent` now is implemented as

    isResultDependent || isParamDependent

We got bitten by this when trying to fix scala#4031.
@Blaisorblade Blaisorblade reopened this Mar 1, 2018
@Blaisorblade
Copy link
Contributor

Was only marked as “fixed” by mistake!

odersky added a commit to dotty-staging/dotty that referenced this issue Mar 17, 2018
odersky added a commit to dotty-staging/dotty that referenced this issue Mar 19, 2018
odersky added a commit to dotty-staging/dotty that referenced this issue Mar 20, 2018
odersky added a commit to dotty-staging/dotty that referenced this issue Mar 28, 2018
@Blaisorblade
Copy link
Contributor

Been puzzling again over this, and had to (re)discover that lazy val p: A { type L <: Nothing } = p will initialize p to null as discussed in #1856.

Blaisorblade pushed a commit to dotty-staging/dotty that referenced this issue Dec 2, 2018
The first attempt required changing z1720.scala; but that became unnecessary
after aligning isRealizable with isStable.
A TermRef is stable if its underlying type is stable. Realizability should
behave the same way.
Blaisorblade pushed a commit to dotty-staging/dotty that referenced this issue Dec 2, 2018
The first attempt required changing z1720.scala; but that became unnecessary
after aligning isRealizable with isStable.
A TermRef is stable if its underlying type is stable. Realizability should
behave the same way.
odersky added a commit to dotty-staging/dotty that referenced this issue Dec 5, 2018
 - Skolemize if not realizable, but use an approximating map
   that avoids using such skolems as paths (because that would
   be unsound).
odersky added a commit to dotty-staging/dotty that referenced this issue Dec 5, 2018
 - Skolemize if not realizable, but use an approximating map
   that avoids using such skolems as paths (because that would
   be unsound).
@odersky
Copy link
Contributor

odersky commented Dec 5, 2018

This was all a red herring I am afraid. You need null. The code as given produces an infinite loop (for p), not a class cast exception. So do the variants developed in #4036.

So since you need null to produce the error the right answer is "wait for null-safety in the type system".

@odersky
Copy link
Contributor

odersky commented Dec 5, 2018

Revisit once we have null-safety. Then it will make a good test case, with the change:

lazy val p: A { type L <: Nothing } = null

@odersky
Copy link
Contributor

odersky commented Dec 5, 2018

[Props to Adriaan for putting me on the right track here]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants