-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix how intersected bounds are added to Constraint #16591
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
ebf01a4
to
1a76585
Compare
1a76585
to
f831b21
Compare
f831b21
to
7ef4054
Compare
case AndType(bound1, bound2) if contains(bound1) ^ contains(bound2) => | ||
add(bound1) && add(bound2) |
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 understand what's going on here but it seems incorrect. According to the documentation comment this case can only be hit if bound
is a lower bound, so we're trying to add the following constraint:
param >: bound1 & bound2
And this case splits this into two two constraints:
param >: bound1
param >: bound2
Which is equivalent to:
param >: bound1 | bound2
So we are over-constraining which can prevent some possible solutions (I can't instantiate param
to bound1
or bound2
or their intersection anymore, unless bound1 =:= bound2
).
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.
No, I'm splitting P >: B & Q
, where P
and Q
are params and B
isn't, into P >: B
constraint and Q < P
ordering, which allows for P =:= Q
unification to go through.
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 still leads to overconstraining, here's an example which compiled before this change but not after:
class LT[-X, Y]
object LT {
given lt[X <: Y, Y]: LT[X, Y] = ???
}
trait B
def foo[Q, P](using LT[B & Q, P])(p: P, q: Q): P = p
def test(b: B, any: Any): B =
foo(b, any)
Setting P =:= Q
means that the method is forced to return Any
, even though the only thing we specified is that B & Any <: B
case AndType(bound1, bound2) if contains(bound1) ^ contains(bound2) => | ||
add(bound1) && add(bound2) |
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 still leads to overconstraining, here's an example which compiled before this change but not after:
class LT[-X, Y]
object LT {
given lt[X <: Y, Y]: LT[X, Y] = ???
}
trait B
def foo[Q, P](using LT[B & Q, P])(p: P, q: Q): P = p
def test(b: B, any: Any): B =
foo(b, any)
Setting P =:= Q
means that the method is forced to return Any
, even though the only thing we specified is that B & Any <: B
I can't remember what approach I was taking before going down this split-the-bound approach. Isn't it still a problem that the bounds are recursive? But the issue it's fixing is kind of niche, so I guess I can leave this for now. |
No description provided.