Skip to content

False error in pattern matching "pattern's type TC[_] is more specialized than the right hand side expression's type TC[?]" #23274

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

Open
smarter opened this issue May 27, 2025 · 0 comments

Comments

@smarter
Copy link
Member

smarter commented May 27, 2025

Compiler version

3.7.0 (and latest nightly)

Minimized code

//> using scala 3.7.0

trait TC[A]

object Test:
  def test1(foo: TC[?]): Unit = foo match
    case _: TC[?] => // ok


  def test2: Unit =
    for
      (_: TC[?]) <- List[TC[?]]() // error
    do ()

Output

-- Error: try/ttp.scala:10:7 ---------------------------------------------------
10 |      (_: TC[?]) <- List[TC[?]]() // error
   |       ^^^^^^^^
   |pattern's type TC[_] is more specialized than the right hand side expression's type TC[?]
   |
   |If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
   |which will result in a filtering for expression (using `withFilter`).
   |This patch can be rewritten automatically under -rewrite -source 3.2-migration.

Expectation

No error, the pattern is not more specialized and will always match.

The problem is easier to debug if we give a name to the type pattern instead of using ?:

    for
      (_: TC[xx]) <- List[TC[?]]() // error
    do ()
10 |      (_: TC[xx]) <- List[TC[?]]() // error
   |       ^^^^^^^^^
   |pattern's type TC[xx] is more specialized than the right hand side expression's type TC[?]

The subtype check that fails is

|| pt.stripNamedTuple <:< pat.tpe
because pat is Typed(Ident(_),AppliedTypeTree(Ident(TC),List(Bind(xx,Ident(_))))) with pat.tpe being AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),trait TC),List(TypeRef(NoPrefix,type xx)))

In both cases, the problem is that pattern matching introduces a local TypeRef and symbol to represent the pattern-bound type variable (if we write ? then we get a pattern-bound type variable called _ which is a bit confusing). They are introduced in:

val sym = newPatternBoundSymbol(name, symTp, tree.span)
for named pattern-bound type variables and in
case tree1: TypeBoundsTree =>
// Associate a pattern-bound type symbol with the wildcard.
// The bounds of the type symbol can be constrained when comparing a pattern type
// with an expected type in typedTyped. The type symbol and the defining Bind node
// are eliminated once the enclosing pattern has been typechecked; see `indexPattern`
// in `typedCase`.
val boundName = WildcardParamName.fresh().toTypeName
val wildcardSym = newPatternBoundSymbol(boundName, tree1.tpe & pt, tree.span)
untpd.Bind(boundName, tree1).withType(wildcardSym.typeRef)
for wildcards (the comment claims that they are subsequently eliminated by indexPattern but this is no longer the case since 7801c57 which harmonized non-wildcard and wildcard handling).

This could be fixed by carefully transforming pattern types in checkIrrefutable to replace TypeRefs of pattern-bound type variables by their bounds. That feels a bit ad-hoc and inefficient, but I can't think of a more general fix that wouldn't break actual use of pattern-bound type variables.

Workaround

Define type AnyTC = TC[?] and match on AnyTC.

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

1 participant