Skip to content

Fix #5077: avoid pattern-bound type for selectors #10672

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

Merged
merged 1 commit into from
Dec 10, 2020

Conversation

liufengyun
Copy link
Contributor

Fix #5077: avoid pattern-bound type for selectors

Given the following definition:

trait Is[A]
case object IsInt extends Is[Int]
case object IsString extends Is[String]
case class C[A](is: Is[A], value: A)

and the pattern match:

(x: C[_]) match
case C(IsInt, i) => ...

The typer (enhanced with GADT) will infer C[A$1] as the type of the
pattern, where A$1 =:= Int.

The patternMatcher generates the following code:

case val x15: C[A$1] =
  C.unapply[A$1 @ A$1](x9.$asInstanceOf$[C[A$1]])
case val x16: Is[A$1] = x15._1
case val x17: A$1 = x15._2        // erase to `Int`
if IsInt.==(x16) then
  {
    case val i: A$1 = x17
...
  }

Note that x17 will have the erased type Int. This is incorrect: it
may only assume the type Int if the test is true inside the block.
If the test is false, we will get an type cast exception at runtime.

To fix the problem, we replace pattern-bound types by Any for
selector results:

case val x15: C[A$1] =
  C.unapply[A$1 @ A$1](x9.$asInstanceOf$[C[A$1]])
case val x16: Is[A$1] = x15._1
case val x17: Any = x15._2
if IsInt.==(x16) then
  {
    case val i: A$1 = x17.$asInstanceOf$[A$1]
    ...
  }

The patternMatcher will then use a type cast to pass the selector
value for nested unapplys or assign it to bound variables.

@dwijnand
Copy link
Member

dwijnand commented Dec 7, 2020

Isn't this wrong because == is user-definable?

@liufengyun
Copy link
Contributor Author

Isn't this wrong because == is user-definable?

Do you mean the first pattern match? Yes, it should be allowed. #5077 (comment) . However, I think it's also fair for the compiler to be incomplete and reject the code. See #9740.

@liufengyun liufengyun added this to the 3.0.0-M3 milestone Dec 7, 2020
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise LGTM

@odersky odersky assigned liufengyun and unassigned odersky Dec 9, 2020
@liufengyun liufengyun force-pushed the fix-5077 branch 2 times, most recently from 63d0580 to 9d72a66 Compare December 9, 2020 21:22
Given the following definition:

    trait Is[A]
    case object IsInt extends Is[Int]
    case object IsString extends Is[String]
    case class C[A](is: Is[A], value: A)

and the pattern match:

    (x: C[_]) match
    case C(IsInt, i) => ...

The typer (enhanced with GADT) will infer `C[A$1]` as the type of the
pattern, where `A$1 =:= Int`.

The patternMatcher generates the following code:

    case val x15: C[A$1] =
      C.unapply[A$1 @ A$1](x9.$asInstanceOf$[C[A$1]])
    case val x16: Is[A$1] = x15._1
    case val x17: A$1 = x15._2        // erase to `Int`
    if IsInt.==(x16) then
      {
        case val i: A$1 = x17
	...
      }

Note that `x17` will have the erased type `Int`. This is incorrect: it
may only assume the type `Int` if the test is true inside the block.
If the test is false, we will get an type cast exception at runtime.

To fix the problem, we replace pattern-bound types by `Any` for
selector results:

    case val x15: C[A$1] =
      C.unapply[A$1 @ A$1](x9.$asInstanceOf$[C[A$1]])
    case val x16: Is[A$1] = x15._1
    case val x17: Any = x15._2
    if IsInt.==(x16) then
      {
        case val i: A$1 = x17.$asInstanceOf$[A$1]
        ...
      }

The patternMatcher will then use a type cast to pass the selector
value for nested unapplys or assign it to bound variables.
@liufengyun liufengyun merged commit b9f9347 into scala:master Dec 10, 2020
@liufengyun liufengyun deleted the fix-5077 branch December 10, 2020 16:02
@Kordyjan Kordyjan modified the milestones: 3.0.0-M3, 3.0.0 Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unexpected behavior for pattern matching on generic case class
5 participants