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

Missing generic tuple extractors in patterns #13968

Closed
nicolasstucki opened this issue Nov 17, 2021 · 6 comments · Fixed by #14296
Closed

Missing generic tuple extractors in patterns #13968

nicolasstucki opened this issue Nov 17, 2021 · 6 comments · Fixed by #14296

Comments

@nicolasstucki
Copy link
Contributor

Compiler version

3.1.0 with -Ycheck:all

Minimized code

object Bar {
  def unapply(x: Any): Option[Int *: Int *: EmptyTuple] = ???
}

def test =
  "" match
    case Bar((a, b)) =>

Output

exception while typing {
  case val x3: Int *: Int *: EmptyTuple = x2.get
  case val x4: Int *: Int *: EmptyTuple = x3
  if x4.ne(null) then 
    {
      case val a: Int = x4._1.$asInstanceOf[Int]
      case val b: Int = x4._2.$asInstanceOf[Int]
      return[matchResult1] 
        {
          ()
        }
    }
   else ()
} of class class dotty.tools.dotc.ast.Trees$Block # -1
...
Exception in thread "main" java.lang.AssertionError: assertion failed: symbols differ for x4._1
was                 : getter _1
alternatives by type:  of types 
qualifier type      : (x4 : Int *: Int *: EmptyTuple)
tree type           : (x4._1 : => T1) of class class dotty.tools.dotc.core.Types$CachedTermRef while compiling tests/pos-macros/contruct-desturct/DestructExpr.scala
java.lang.AssertionError: assertion failed: symbols differ for x4._1
was                 : getter _1
alternatives by type:  of types 
qualifier type      : (x4 : Int *: Int *: EmptyTuple)
tree type           : (x4._1 : => T1) of class class dotty.tools.dotc.core.Types$CachedTermRef

The issue is that we have a Int *: Int *: EmptyTuple and not a Tuple2[Int, Int] and therefore we do not have the member _1. We do have the apply(0) which does the same and erases/optimized to _1 in this case.

Expectation

We should generate x4.apply(0) instead of x4._1. This should make it possible to also scale above the 22 tuple limit.

@nicolasstucki
Copy link
Contributor Author

An alternative is to cast the tuple type

  case val x3: Int *: Int *: EmptyTuple = x2.get
  case val x4: Tuple2[_, _] = x3.asInstanceOf[Tuple2[_, _]]
  case val x3: Int *: Int *: EmptyTuple = x2.get
  case val x4: Tuple2[Int, Int] = x3.asInstanceOf[Tuple2[Int, Int]] @unchecked

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Nov 17, 2021

Or we could try to make Tuple2[Int, Int] =:= Int *: Int *: EmptyTuple instead of only Tuple2[Int, Int] <:< Int *: Int *: EmptyTuple.

@dwijnand
Copy link
Member

We do have the apply(0) which does the same and erases/optimized to _1 in this case.

In the JIT or in our transform phases/backend? Because it looks like it's delegating to a runtime call.

Having recently done a pass over the extraction logic, I can't explain how case Bar((a, b)) => even passes typing. Tuple is a Product of arity 0 (no _1/etc methods, as you mentioned), so how is a Tuple2.unapply qualifying?

@dwijnand
Copy link
Member

Oh now I get it. It doesn't qualify in the version that I thought I was reading, which is the version you have in #13969. Phew, sanity recovered. I mean, there's still a subpattern of Tuple2.unapply being allowed to consume Tuple, but I guess that's where your Tuple2[Int, Int] <:< Int *: Int *: EmptyTuple somehow comes into play.

@nicolasstucki
Copy link
Contributor Author

We do have the apply(0) which does the same and erases/optimized to _1 in this case.

In the JIT or in our transform phases/backend? Because it looks like it's delegating to a runtime call.

We optimize those in the TupleOptimizations phase. If the size of the generic tuple is known we we optimize the operations to use the TupleN operations directly. Here is how we handle the apply https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala#L142-L167

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Nov 18, 2021

Having recently done a pass over the extraction logic, I can't explain how case Bar((a, b)) => even passes typing. Tuple is a Product of arity 0 (no _1/etc methods, as you mentioned), so how is a Tuple2.unapply qualifying?

It seems the pattern matching logic identifies at some point generic tuples of know size N as TupleN. But then assumes that it is a TupleN and generates the code for a TupleN.

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

Successfully merging a pull request may close this issue.

3 participants