-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #6288: Allow Singletons in Unions #6299
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
Conversation
For reference, here's the open issue about this: #1551 |
test performance please |
performance test scheduled: 1 job(s) in queue, 0 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/6299/ to see the changes. Benchmarks is based on merging with master (c3e88b7) |
test performance please |
performance test scheduled: 1 job(s) in queue, 0 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/6299/ to see the changes. Benchmarks is based on merging with master (c3e88b7) |
test performance please |
performance test scheduled: 1 job(s) in queue, 0 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/6299/ to see the changes. Benchmarks is based on merging with master (25a3773) |
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/6299/ to see the changes. Benchmarks is based on merging with master (8e323b1) |
@nicolasstucki I had to disable compiling tasty-interpreters in BootstrappedOnlyCompile since I got errors of this form:
Can you take a look what went wrong here, and hopefully bring them back? |
263091b
to
2e9084e
Compare
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/6299/ to see the changes. Benchmarks is based on merging with master (afb7a8b) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise LGTM
def canCompare(atoms: Set[Type]): Boolean = | ||
ctx.phase.isTyper || { | ||
val hasSkolems = new ExistsAccumulator(_.isInstanceOf[SkolemType]) { | ||
override val stopAtStatic = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a def
tests/pos/i6288.scala
Outdated
val x = 3 | ||
opaque type T = x.type | ||
object T { | ||
def wrap(a: x.type): T = a // Error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intended to be a neg test?
6343bb7
to
50154cf
Compare
I added a commit fixing the checkfile of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rebase to make sure this doesn't conflict with recent changes such as the full bootstrap.
* two different skolems in all phases `p`, where `p.isTyper` is false. | ||
* But in that case comparing two sets of atoms that contain skolems | ||
* for equality would give the wrong result, so we should not use the sets | ||
* for comparisons. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't not using the sets a potential performance problem ? Have you considered having both OrType#atoms
and OrType#atomsUnifySkolems
instead, where the latter would replace all skolems referring to the same type by a single instance ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe skolems are sufficiently rare. If it turns out that we get a performance problem, what you suggest could be a good alternative.
@@ -1499,30 +1542,38 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { | |||
/** The greatest lower bound of a list types */ | |||
final def glb(tps: List[Type]): Type = ((AnyType: Type) /: tps)(glb) | |||
|
|||
def widenInUnions(implicit ctx: Context): Boolean = ctx.scala2Mode || ctx.erasedTypes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have examples of Scala 2 code that breaks without the scala2Mode
here ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tried. But it's better to be conservative, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, all these inference differences (that we'll have to document at some point) make it more risky for a codebase to turn off Scala2 mode since there's a non-zero chance any of them could cause a slient change of runtime behavior (e.g. because a different implicit ends up being selected). They're also only lightly tested since most of our tests are run without Scala2 mode enabled, meaning a higher risk of unforeseen interactions. So I'd limit them to things that significantly help in cross-compilation, but we can revisit this some other time.
val isSingle1 = tp1.isInstanceOf[SingletonType] | ||
val isSingle2 = tp2.isInstanceOf[SingletonType] | ||
return { | ||
if (tp2w eq tp2) orDominator(tp1w | tp2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be helpful to add comments like // 2.1
above conditions to relate them with the comment above and make sure they stay in sync in the future.
else if (tp2w frozen_<:< tp1w) orDominator(tp1 | tp2w) | ||
else if (isSingle1 && !isSingle2) orDominator(tp1w | tp2) | ||
else if (isSingle2 && !isSingle1) orDominator(tp1 | tp2w) | ||
else if (tp1 frozen_<:< tp2w) tp2w |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a case for 2.5 looking like else if (tp2 frozen_<:< tp1w) tp1w
missing here ?
else if (isSingle1 && !isSingle2) orDominator(tp1w | tp2) | ||
else if (isSingle2 && !isSingle1) orDominator(tp1 | tp2w) | ||
else if (tp1 frozen_<:< tp2w) tp2w | ||
else orDominator(tp1w | tp2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2.6 says "pick tp1", that doesn't seem to match the code.
tp | ||
} | ||
|
||
/** Widen all top-level singletons reachable by dealising |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** Widen all top-level singletons reachable by dealising | |
/** Widen all top-level singletons reachable by dealiasing |
@nicolasstucki I tried to update the check file but still get errors for the runMacros test. Can you take a look? Thanks! |
Widen in lubs only if - the type is unstable, or - we are after erasure This need to be complemented by performance work as otherwise complexity of subtype checking will explode.
We also need to undo the RefinedType self type of opaque type companions. Otherwise, programs die in erasure if tracing is enabled.
Since we do not widen singletons in unions, it is now easy to get large union types consisting of singletons. We need to specialize the code dealing with them in order not to get deep subtype recursions and drastic compilation speed slowdowns.
This was the situation before the PR. I thought that simply checking whether the instance is a subtype of the bound is equivalent, but compare-singletons.scala shows that this is not the case. Here, we have a type variable X such that `r.type <:< X <:< Singleton` where `r: R` and `R <: Singleton`. With the previous changes in this PR, we instantiate `X` to `R`. With the revert in this commit we instantiate it to `r.type` instead.
This relieves some of the load of `join` and offers more opportunities to improve union types by constraining further. Without this step we might lose some improvements compared to the situation before where we always widened singletons in OrTypes. I don't have a test case that demonstrates the difference, though.
This was detected when failing to compile the 2.13 library. The problem is minimized in test pos/padTo.scala. The failure arose because with the introduction of singletons in unions we get more "interesting" union types to approximate. But it can be also produced without singleton types. Further join refinements
Using --update-checkfiles
No description provided.