Skip to content
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

Regression: tuple inline matching and head/tail #21277

Closed
ncreep opened this issue Jul 25, 2024 · 4 comments
Closed

Regression: tuple inline matching and head/tail #21277

ncreep opened this issue Jul 25, 2024 · 4 comments

Comments

@ncreep
Copy link

ncreep commented Jul 25, 2024

Compiler version

3.4.2

Minimized code

//> using scala "3.4.2"

inline def foo[T <: Tuple](t: T) =
  inline t match
    case tt: (String *: EmptyTuple) =>
      val x: String = tt.head
      val y: EmptyTuple = tt.tail

Output

[error] Found:    Tuple.Head[T & String *: EmptyTuple]
[error] Required: String
[error]
[error] where:    T is a type in method foo with bounds <: Tuple
[error]
[error]
[error] Note: a match type could not be fully reduced:
[error]
[error]   trying to reduce  Tuple.Head[T & String *: EmptyTuple]
[error]   failed since selector T & String *: EmptyTuple
[error]   does not uniquely determine parameter x in
[error]     case x *: _ => x
[error]   The computed bounds for the parameter are:
[error]     x <: String
[error]       val x: String = tt.head
[error]                       ^^^^^^^
[error] ./tuple_matching.scala:5:27
[error] Found:    Tuple.Tail[T & String *: EmptyTuple]
[error] Required: EmptyTuple
[error]
[error] where:    T is a type in method foo with bounds <: Tuple
[error]
[error]
[error] Note: a match type could not be fully reduced:
[error]
[error]   trying to reduce  Tuple.Tail[T & String *: EmptyTuple]
[error]   failed since selector T & String *: EmptyTuple
[error]   does not uniquely determine parameter xs in
[error]     case _ *: xs => xs
[error]   The computed bounds for the parameter are:
[error]     xs <: EmptyTuple
[error]       val y: EmptyTuple = tt.tail

Expectation

The code should compile.

This is a regression compared to 3.3.3 where the same code compiles as expected.

Also, the following workaround works for some reason:

inline def bar[T <: Tuple](t: T) =
  inline t match
    case tt: (String *: EmptyTuple) =>
      val ttt: String *: EmptyTuple = tt

      val x: String = ttt.head
      val y: EmptyTuple = ttt.tail

Note the explicitly typed assignment to ttt.

@ncreep ncreep added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 25, 2024
@sjrd
Copy link
Member

sjrd commented Jul 26, 2024

That looks expected to me. The previous reduction relied on some unsound behavior of match types, which we fixed since.

@Gedochao
Copy link
Contributor

Seems we are treating it as intentional behaviour. Closing this.

@Gedochao Gedochao closed this as not planned Won't fix, can't repro, duplicate, stale Jul 26, 2024
@ncreep
Copy link
Author

ncreep commented Jul 26, 2024

Thanks for the quick response.

@sjrd, can you please elaborate about this error?
I see there's this superfluous T & String *: EmptyTuple type assigned to tt. Why isn't it just String *: EmptyTuple as implied by the case statement?

Is there some recommended way of working with inline tuple matching that circumvents this error? Or the workaround I mentioned the only way to do it?

For my real use case, I wouldn't mind using unapply in the match directly, but the actual match is on type parameters, like so:

inline def foo[T <: Tuple](t: T) =
  inline t match
    case (xx: h) *: (yy: t)=>
      val x: h = xx
      val y: t = yy

And that doesn't seem to work either as h and t are not recognized as type variables.

Thanks

@ncreep
Copy link
Author

ncreep commented Jul 31, 2024

For anyone else stumbling on this, found a slightly more pleasant workaround for this issue:

inline def bar[T <: Tuple](t: T) =
  inline t match
    case tt: (h *: t) =>
      val head *: tail = tt
      
      // `head` has type `h`
      // `tail` `has type `t`

But I still think that this behavior renders Tuple.head/Tuple.tail unusable in generic inline contexts.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants