-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Break circularity in TypeBounds with LazyRef wraps #14288
Conversation
@smarter do you have any better ideas on how to avoid the circular problem here? |
What are we recursing on here that leads to a cycle exactly? |
enum Foo[+H[_]]:
case Bar[F[_]](f: Foo[F]) extends Foo[F]
case Baz()
def test: Foo[H] = this match
case Bar(Bar(f)) => Bar(f)
case _ => this It's a case of TypeBounds with circular infos:
|
Seems weird. I don't even see how we can construct TypeBounds with circular info, I assume there are actually type variables in the info somewhere? (print without using |
If type variables turn out to be involved, this is likely caused by the handling of TypeVar in avoid (https://github.com/lampepfl/dotty/blob/9e14f5fba1acd993b8fcd99e4afe64fc17d37d30/compiler/src/dotty/tools/dotc/core/TypeOps.scala#L508-L512) which maps a type variable to one of its bound without actually instantiating the type variable which I always found weird. |
Nope, no variables, just type refs:
|
Sounds like a problem with the gadt logic that generates these types then? |
Oh yeah? Why, what should they be instead? I thought the types seemed legitimate, just tricky to traverse. |
I don't have a good enough grasp of the gadt logic to say, but as far as I can tell no code outside of it can produce type bounds like that so it's natural that the compiler isn't setup to deal with them (and there might be many more type maps where this would be a problem): scala> def foo[A >: B, B <: A] = {}
-- Error:
1 |def foo[A >: B, B <: A] = {}
| ^
|illegal cyclic type reference: lower bound B of type A refers back to the type itself
scala> class X { type A >: B; type B <: A }
-- Error:
1 |class X { type A >: B; type B <: A }
| ^
|illegal cyclic type reference: lower bound X.this.B of type A refers back to the type itself |
There should be no circular info in type bounds. Circular upper bounds are allowed (i.e. that's what an F-bound is), but internally they must be protected with a LazyRef. So we should find out how and where these illegal bounds are generated. |
9e7f1e0
to
229f065
Compare
@@ -127,7 +127,20 @@ trait ConstraintHandling { | |||
* of some param when comparing types might lead to infinite recursion. Consider `bounds` instead. | |||
*/ | |||
def fullBounds(param: TypeParamRef)(using Context): TypeBounds = | |||
nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) |
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 is likely still not the correct place to fix this, constraints shouldn't be cyclic (although the assert for this is disabled by default for performance: https://github.com/lampepfl/dotty/blob/3c8200671995ea75b97b493081cc4e51109cfd04/compiler/src/dotty/tools/dotc/config/Config.scala#L21-L24), it's possible the logic in GadtConstraint.scala violates this.
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.
Thanks, I'll check.
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.
Enabling doesn't seem to trigger anything. Either it fails without my "half answer" workaround or it passes with - no check error. Are we sure it's a cycle in the constraints? Need to investigate more.
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.
So inference first creates the type refs while typing the patterns (in maximizeType
via typedUnApply
) as F$1 <: F
and F$2 <: F$1
. Just a little later runs over the patterns again in indexPattern
where it calls fullBounds
leading to F$1(param)1 >: F$2 <: F
and F$2(param)1 <: F$1
.
So is it circular constraints? 🤔
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.
Yeah, either gadt.fullBounds should not produce cycles like this, or we shouldn't use it here, wdyt @abgruszecki ?
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.
The way I see it, fullLowerBound
aren't wrong in their answers. But put together they lead to a mutual reference. So fixing this in fullBounds by inspecting the results seems reasonable to me.
Hopefully I've fixed the test suite now.
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.
But can this be triggered with non-gadt constraints?
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 don't know. 🤷🏼♂️ But GadtConstraint redefines its fullLowerBound and fullUpperBound so possibly not?
229f065
to
9511a91
Compare
9511a91
to
d635a56
Compare
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 don't think this should get in until we actually understand if this is specific to gadt and if it's working around a bug or the correct thing to do.
I'm concerned that those answers might not come readily. Would you be happy to get the fix in even if we don't have those answers in, let's say, a month? I don't know the answers to your questions, I'm sorry. But it's peeving to put effort into fixing a user-reported bug to then the PR blocked like so. |
I'm not comfortable merging code no one understand, and I don't have the time to try to understand it myself, this is why I asked for Alex's input but someone else could fill in too. |
I'm now not happy with this fix either. |
No description provided.