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

Fix #4031: Check arguments of dependent methods for realizability #4036

Closed
wants to merge 15 commits into from

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Feb 26, 2018

Also some additions to realizability tests

@odersky
Copy link
Contributor Author

odersky commented Feb 26, 2018

I now think we should try to avoid this split into isStable (checked in Typer) and checkRealizable in PostTyper, at least as far as lazy vals are concerned. We should try to test for realizibility when checking whether a lazy val is stable. If this runs into cycles, we can always back out by being conservative, i.e. assume non-stable in case of cycles. I think that's at least worth a try. The alternatives look all even less appealing.

@Blaisorblade
Copy link
Contributor

Treating lazy vals as unstable here would already be an improvement. Is “running into cycles” insensitive to compilation order/separate compilation, or does it have a suitable conservative approximation?

Beyond “what we can implement” we should probably think of what we can specify and explain to an (advanced) user.


def intToString(i: Int): String = xToString(i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-TODO: dig how this test broke and if this affected other tests for #50 and #1051. I suspect that’s because the logic changed at some point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test was modified in 955b04a (#1106), which also seems to have removed a test in tests/neg/i1050c.scala; but I guess the tests had stopped passing because of other changes. I just reenabled that test on this branch, but this PR doesn't fix it yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test did not break. It's masked by the other failures. Taken alone, it would be reported later.

@@ -205,6 +205,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
Checking.checkInstantiable(tree.tpe, nu.pos)
withNoCheckNews(nu :: Nil)(super.transform(tree))
case _ =>
tree.fun.tpe.widen match {
case mt: MethodType if mt.isDependent || mt.isParamDependent =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotta s/isDependent/isResultDependent/ because I merged #4039.



def main(args: Array[String]): Unit = {
//println(coerce("Uh oh!"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment back in, that's the actual ClassCastException-triggerer.

@@ -4,7 +4,7 @@ object Import {
trait B { type L >: Any}
trait U {
lazy val p: B
locally { val x: p.L = ??? } // old-error: nonfinal lazy
locally { val x: p.L = ??? } // error: nonfinal lazy
locally {
import p._ // error: Import.B(U.this.p) is not a legal path
val x: L = ??? // old-error: nonfinal lazy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably this should also be re-enabled, but at least the import p._ fails.

@@ -4,7 +4,7 @@ object Import {
trait B { type L >: Any}
trait U {
lazy val p: B
locally { val x: p.L = ??? } // old-error: nonfinal lazy
locally { val x: p.L = ??? } // error: nonfinal lazy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p.L has bad bounds, and this test was disabled (by mistake) by unrelated PR #1106 (probably because it had broken), so re-enabling this test.

Since it isn't about a method call it's not addressed by the current fix, but it's still related to realizability checks I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it was because the tests in 1050c are done during typer and the non-final lazy is detected later in PostTyper, so not detected when there are typer errors. The comment was misleading.
There are tests like this in i1050.scala, which get reported correctly.

@odersky
Copy link
Contributor Author

odersky commented Mar 17, 2018

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/4036/ to see the changes.

Benchmarks is based on merging with master (eeff384)

@odersky
Copy link
Contributor Author

odersky commented Mar 19, 2018

Last commit should fix the broken build.

@Blaisorblade
Copy link
Contributor

Cherry-picked last commit in #4141.

isType || !is(Erased) && (is(Stable) || !(is(UnstableValue) || info.isInstanceOf[ExprType]))
final def isStable(implicit ctx: Context) = {
def isUnstableValue = is(UnstableValue) || info.isInstanceOf[ExprType]
isType || is(Stable) || !isUnstableValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ To be sure: we'd get different results for Stable | Erased but that combo is currently illegal (and Erased symbols might become Stable in the future anyway).

@@ -155,6 +156,12 @@ object Types {
case _ => false
}

/** Does this type denote a realizable stable reference? Much more expensive to checl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checl->check

@@ -4136,7 +4145,7 @@ object Types {
forwarded.orElse(
range(super.derivedSelect(tp, preLo), super.derivedSelect(tp, preHi)))
case _ =>
super.derivedSelect(tp, pre)
if (pre == defn.AnyType) pre else super.derivedSelect(tp, pre)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: understand

else {
val widenedArgType = argType.widen
if (realizability(widenedArgType) == Realizable)
tp.substParam(pref, SkolemType(widenedArgType))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

widenedArgType can be realizable when argType isn't if argType is a lazy/erased value.
If argType is a normal value it's realizable always, even if widenedArgType isn't.

If neither is realizable, one could try harder, with an even wider type, but we don't have a reliable way to get at it — there are wider types (such as Any) but it's unclear there's a smallest wider type, since the class hierarchy is a DAG.

@@ -3,7 +3,7 @@ package test
class Thing {
def info: Info[this.type] = InfoRepository.getInfo(this)
def info2: Info[this.type] = {
def self: this.type = this
val self: this.type = this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it seems that this.type should be as realizable as this, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you are right. I added a further tweak to support this in 8389bea

@odersky
Copy link
Contributor Author

odersky commented Mar 20, 2018

Based on #4108, because that way we could remove a puzzler in derivedSelect.

@Blaisorblade
Copy link
Contributor

Should rebase on top of #4108 (now merged). Can try next week.

if (r == Realizable) sym.setFlag(Stable)
r
}
if (r == Realizable || tp.info.isStableRealizable) Realizable else r
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r == Realizable || can be dropped without changing meaning. In fact, it's unclear why we should check tp.info.isStableRealizable at this point — if this is necessary, it seems to warrant a comment. Maybe this subtly depends on setFlag influencing isStableRealizable.

Should we check tp.info.isStableRealizable first and skip all the other expensive computations? That doesn't set the sym.is(Stable) cache, but it can be set on request the next time.*

Otherwise, should tp.info.isStableRealizable be used when computing r?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Playing with this myself.

@Blaisorblade
Copy link
Contributor

Status: I modified the checking algorithm (for small optimizations and to cache everything cacheable) but it should check the same "typing rules" — except for any undiscovered bugs due to internal mutations/cycles/...

I removed a code which seemed to repeat a call to realizability(tp.info), but I'm not sure if anything could actually trigger it before.

The changes might make the code faster in some cases — but the reason I made them was to make the code IMHO more sensible: now symbols are marked as is(Stable) in any of the conditions that show the symbol is Stable.

TODO: urgently rename isStable to something else.

@Blaisorblade
Copy link
Contributor

Martin mentioned he will look at this once I assign him, so I should decide what to do about the small issues I wanted to fix.

odersky and others added 11 commits August 16, 2018 12:06
Add the rule T <: Any for any *-Type T. This was not include fully before. We
did have the rule that T <: Any, if Any is in the base types of T. However,
it could be that the base type wrt Any does not exist. Example:

    Any#L <: Any

yielded false before, now yields true. This error manifested itself in i4031.scala.

With the new rule, we can drop again the special case in derivedSelect.
A TermRef is stable if its underlying type is stable. Realizability should
behave the same way. This obviates the change in z1720.scala.
I had a TypeError crash in refchecks after screwing up a typeclass encoding in a particularly bad way.
This commit reports an error instead.
That is, set is(Stable) for symbols that satisfy isStable.
I wrote this because I feared (incorrectly) exponential slowdowns in
realizability checking for this code. But debugging suggests that the
complexity of realizability checking is constant in the size of these
expressions (even after I disable caching of `Stable`).

Beware 1: these expressions almost smash the stack by sheer size.

Beware 2: this fails with `-Yno-deep-subtypes`, but simply because the checking
heuristics assumes people don't try to do this.
This reduces confusion with `Type.isStable`. I'm tempted to call this `isPure`.
This follows scala#4360. I'd love to add a test but scala#4360 has none, so I'm not 100%
sure what it'd look like.
This reverts commit 283f261 because it
introduces failures (which I don't get yet), as also shown in
http://dotty-ci.epfl.ch/lampepfl/dotty/5085/4.
@Blaisorblade
Copy link
Contributor

TODO: check this PR fixes https://twitter.com/propensive/status/1031419434320179200?s=21:

implicitly[(A with B)#Z =:= Int]
implicitly[(A with B)#Z =:= Char]
implicitly[(A with B)#Z =:= (B with A)#Z]

Blaisorblade added a commit to dotty-staging/dotty that referenced this pull request Dec 2, 2018
After porting the commit from scala#4036, this line newly gives an error. Might be
OK, investigate.
Blaisorblade added a commit to dotty-staging/dotty that referenced this pull request Dec 2, 2018
After porting the commit from scala#4036, this line newly gives an error. Might be
OK, investigate.
Blaisorblade added a commit to dotty-staging/dotty that referenced this pull request Dec 2, 2018
After porting the commit from scala#4036, this line newly gives an error. Might be
OK, investigate.
@Blaisorblade
Copy link
Contributor

Closed in favor of #5558 and #5559.

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

Successfully merging this pull request may close these issues.

None yet

3 participants