-
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
Match type bounded by type with argument(s) cannot be reduced to satisfy =:=
check
#17149
Comments
For some reason, we don't get a reduction: adapt to subtype A =:= A !<:< Ext[Seq[Int]] =:= Int
>>>> StoredError: Found: A =:= A
Required: Ext[Seq[Int]] =:= Int
where: A is a type variable
adapt to subtype A =:= A !<:< Ext[Seq[Int]] =:= Int
>>>> StoredError: Found: A =:= A
Required: Ext[Seq[Int]] =:= Int if we use ==> isSubType Ext[Seq[Int]] <:< Nothing?
==> isSubType Int <:< Nothing?
<== isSubType Int <:< Nothing = false ==> isSubType Ext[Seq[Int]] <:< Nothing?
==> isSubType Any <:< Nothing?
<== isSubType Any <:< Nothing = false So, this works: type Ext[S <: Seq[Any]] = S match
case Seq[t] => t
val a = summon[Ext[Seq[Int]] =:= Any] |
And, unsurprisingly, works in 3.1.3 -- Looks like a duplicate of #15926. |
Also works in tasty-query 😁 |
The whole problematic tree looks like this: ==> S match case Seq[t] => t?
==> isSubType S <:< Seq[t]?
<== isSubType S <:< Seq[t] = false
==> isSubType S <:< Seq[t]?
==> isSubType Seq[Any] <:< Seq[t] (left is approximated)?
==> isSubType Any <:< t?
==> isSubType Any <:< Nothing?
<== isSubType Any <:< Nothing = false
==> isSubType Any <:< Any?
==> isSubType scala.type <:< (scala : scala.type)?
<== isSubType scala.type <:< (scala : scala.type) = true
<== isSubType Any <:< Any = true
<== isSubType Any <:< t = true
<== isSubType Seq[Any] <:< Seq[t] (left is approximated) = true
<== isSubType S <:< Seq[t] = true
==> isSubType Any <:< Any?
==> isSubType (scala : scala.type) <:< scala.type?
<== isSubType (scala : scala.type) <:< scala.type = true
<== isSubType Any <:< Any = true
<== S match case Seq[t] => t = Reduced(Any) So, in the end, we get: type Ext = ([S <: Seq[Any]] =>> Any) 😢 |
Yeah, I looked at that trace again, from that duplicate. My original fix was to disallow, using your trace, |
Disallowing it seems too restrictive, -- and if we try to widen it to I am not sure about the approximation state -- what is it supposed to look like? Seems to me, the approximation is correct because anything other than |
ApproxState maintains whether the LHS has been widened and/or the RHS has been narrowed, and that affects, for instance, whether to consider it as a constraint bound. Here I'm thinking of the check In the trace it (sometimes) appears as "(left is approximated)" (or right), and it disappears when checking args. I was wondering if that was the problem. |
I looked into 3.1.3 trace: ==> isSubType S <:< Seq[t]?
<== isSubType S <:< Seq[t] = false
==> isSubType Any <:< Any?
==> isSubType scala.type <:< (scala : scala.type)?
<== isSubType scala.type <:< (scala : scala.type) = true
<== isSubType Any <:< Any = true
==> isSubType Nothing <:< Any?
<== isSubType Nothing <:< Any = true So it's widening to type Ext[S <: Seq[Int]] = S match
case Seq[t] => t
val a = summon[Ext[Seq[String]] =:= String] UPD: Ah, that's #17168. If we use |val a = implicitly[Ext[Seq[String]] =:= String]
| ^
| Cannot prove that Ext[Seq[String]] =:= String. |
Yeah, not widening the abstract type |
Going back to this comment, I think disallowing is correct: we don't want the match type to reduce to the upper bound, because then we lose the tie to the actually passed type. We want the String in Seq[String], not Any, even though Any is String's upper bound. The match type, as it's defined using an abstract type in the match alias's type lambda, should stay an unreduced match type (because reduction gets stuck). |
I had a look at this with @Decel. The critical PR which led to this situation is #15423, which itself follows up on several previous PRs that go back and forth on the issue. Essentially, there are two interlinked questions
Originally, the answer was yes to both, but one can easily show that that's unsound. Then #13780 disallowed (1), but that caused a lot of regressions of reasonable code that worked before. Then #15423 answered the question by trying two different strategies as shown in this snippet in if caseLambda.exists && matches(canWidenAbstract = false) then
redux(canApprox = true)
else if matches(canWidenAbstract = true) then
redux(canApprox = false) So, we first say no to (1), and if that fails we try again saying yes to (1), but restricting how type variables get instantiated.
But that's super hard to check, so we need an approximation. The approximation we used was that, if at the end of the comparison, a type variable was constrained to a single point, then we can instantiate the type variable to that point. Might look reasonable, but is unsound. The problem was in the example here. We have:
We widen Not yet sure what to do about this. |
Refine criterion when to widen types in match type reduction. We now do not widen if the compared-against type contains variant named type parameters. This is intended to fix the soundness problem in scala#17149. Fixes scala#17149 Todos: - [ ] Check & fix neg test failures - [ ] Add more tests - [ ] Also consider approximating abstract types to lower bounds. This is completely missing so far. There are neither tests nor an implementation. - [ ] Update the docs on match types to explain what goes on here.
Refine criterion when to widen types in match type reduction. We now do not widen if the compared-against type contains variant named type parameters. This is intended to fix the soundness problem in scala#17149. Fixes scala#17149 Fixes scala#15926 Todos: - [ ] Check & fix neg test failures - [ ] Add more tests - [ ] Also consider approximating abstract types to lower bounds. This is completely missing so far. There are neither tests nor an implementation. - [ ] Update the docs on match types to explain what goes on here.
=:=
check=:=
check
I also have a simplified dual form:
The fix should theoretically work on both cases |
Refine criterion when to widen types in match type reduction. We now do not widen if the compared-against type contains variant named type parameters. This is intended to fix the soundness problem in scala#17149. Fixes scala#17149 Fixes scala#15926 Todos: - [ ] Check & fix neg test failures - [ ] Add more tests - [ ] Also consider approximating abstract types to lower bounds. This is completely missing so far. There are neither tests nor an implementation. - [ ] Update the docs on match types to explain what goes on here.
Pointed out by @DmytroMitin in the following post:
https://stackoverflow.com/questions/75837775/in-scala-2-or-3-is-there-a-higher-kind-argument-extractor-without-using-match-t/75842531#75842531
If I change the definition of
Ext
as follow, the consequence will differ:The text was updated successfully, but these errors were encountered: