-
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
Regressions in fingo/spata
for match types
#21013
Comments
Minimization: type Select[K <: String] = Tuple match
case K *: _ => Int
def Test: Unit =
val labels: List[String] = ???
val vals = labels.map: l =>
??? : Select[l.type] // error: Required: Tuple match { case ? <: String *: _ => Int } <: Int
Tuple.fromArray(vals.toArray) // error: No ClassTag available for T |
The issue comes down to applications of higher kinded types to wildcard arguments: type M1[K] = Double match
case K => Int
type M2[K] = Double match
case Option[K] => Int
def Test =
// Issue A: `M1[Int] <:< M1[?]` fails
val x1: M1[?] = ??? // application to `?` is accepted
val x2: M1[Int] = x1 // error
// Issue B: `M2[?]` reported as unreducible but accepted when manually inlined
type R2 = M2[?] // error: unreducible application to wildcard arguments
type R1 = Int match
case Option[?] => Int // ok
// Original issue
def foo[B](f: String => B): Unit = ???
foo: l =>
??? : M1[l.type] // ok
??? : M2[l.type] // error:
// gets widened to the inlining of M2[?] for the inferred B,
// issue B is somehow avoided, but then runs into issue A |
IMO this is the actual issue: val x1: M1[?] = ??? // application to `?` is accepted I don't think this should be accepted. The expansion is not valid: scala> type M1[K] = Double match { case K => Int }
scala> type M2 = Double match { case ? => Int }
-- [E035] Syntax Error: --------------------------------------------------------
1 |type M2 = Double match { case ? => Int }
| ^
| Unbound wildcard type
|
| longer explanation available when compiling with `-explain`
scala> type M3 = M1[?] clearly is not consistent. |
Agreed. But Although perhaps a separate issue, I still think avoidance yielding the expansion of |
Unclear. In fact it's unclear that substituting a type T = (?, ?) // valid
type U[X] = (X, X) // valid
type V = U[?] // invalid! Analogously, it's possible--and I'd say likely--that the following should hold as well: type T = Double match { case Option[?] => Int } // valid
type U[X] = Double match { case Option[X] => Int } // valid
type V = U[?] // invalid!
Assuming |
Not arguing against this or anything, but this is all still admittedly a bit unclear to me. In the non match type example, the explanation of the error msg states: -- [E043] Type Error: tests/playground/example.scala:3:9 -----------------------
3 |type V = U[?] // invalid!
| ^^^^
|unreducible application of higher-kinded type [X] =>> (X, X) to wildcard arguments
|-----------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| An abstract type constructor cannot be applied to wildcard arguments.
| Such applications are equivalent to existential types, which are not
| supported in Scala 3.
----------------------------------------------------------------------------- but
For clarity, you mean avoiding
Good point! A follow up question would be, we always expect avoidance to yield a type to which the original one conforms? |
Looks like the message incorrect. It should say "non-class type constructor" instead of "abstract type constructor". The message was probably written like that because we allow concrete type constructors that are eta-expansions of classes, but spec-wise we basically consider those as class types.
Yes, I mean to
Yes, that is a strong requirement. Type avoidance must abide by this rule. In fact I become increasingly skeptical of the exemption for match aliases in scala3/compiler/src/dotty/tools/dotc/core/Types.scala Lines 4609 to 4617 in 2ea90c6
There could be a (X, X) even in a case body. Substituting ? for X in such a case is definitely unsound.
|
Looks like the exemption was added in 6871cff by @odersky. Quoting from the commit message:
Well ... I believe it's unsound, actually. |
I think the idea behind 6871cff was that a match type alias where the scrutinee was a wildcard is simply an unreducible match type, which should be sound. But I overlooked the case where the wildcard appears in the patterns, that is indeed unsound. Can we refine the restriction to reject this? |
It's more than just the patterns, though. If you have type F[X, Y] = X match { case Int => (Y, Y) } and you now apply In general, a match type can hide an arbitrary type computation that substitutes its type parameters in arbitrary places, including ones where |
Right. So the only case where the application is sound is if the type variable appears only in the scrutinee. We'd have to go back to the original motivation for introducing this change in 6871cff to decide whether this exemption is still worthwhile, or we should outlaw wildcards also in match aliases. |
Going back to #9999 it seems there was a strong community demand to allow wildcard in match aliases so I believe we need to work hard not to overshoot in the revert or there will be backlash. |
The last good compiler version 3.4.2-RC1-bin-20240229-9fe0111-NIGHTLY
There are 2 issues related to the same reproducer
Reproducer
Outputs
Issue 1
Introduced in #19761 and described in #20972 (comment)
Before that change, the
toArray
was inferred to betoArray[Any]
which even though it allowed for compilation it was probably incorrect, but I'd like to request confirmation.Issue 2
After using explicit `toArray[Any] to mitigate issue 1 in some later versions of compiler
Last good release: 3.4.2-RC1-bin-20240302-c7a0459-NIGHTLY
First bad release: 3.4.2-RC1-bin-20240305-beba585-NIGHTLY
No exact bisect result due to issues with the compiler builds
or with later versions
The text was updated successfully, but these errors were encountered: