diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index a20f11e3cf9c..1be6bbda98fb 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -94,14 +94,15 @@ object PatternMatcher { */ private val initializer = MutableSymbolMap[Tree]() - private def newVar(rhs: Tree, flags: FlagSet): TermSymbol = + private def newVar(rhs: Tree, flags: FlagSet, tpe: Type): TermSymbol = newSymbol(ctx.owner, PatMatStdBinderName.fresh(), Synthetic | Case | flags, - sanitize(rhs.tpe), coord = rhs.span) + sanitize(tpe), coord = rhs.span) // TODO: Drop Case once we use everywhere else `isPatmatGenerated`. /** The plan `let x = rhs in body(x)` where `x` is a fresh variable */ - private def letAbstract(rhs: Tree)(body: Symbol => Plan): Plan = { - val vble = newVar(rhs, EmptyFlags) + private def letAbstract(rhs: Tree, tpe: Type = NoType)(body: Symbol => Plan): Plan = { + val declTpe = if tpe.exists then tpe else rhs.tpe + val vble = newVar(rhs, EmptyFlags, declTpe) initializer(vble) = rhs LetPlan(vble, body(vble)) } @@ -223,6 +224,13 @@ object PatternMatcher { /** Plan for matching `scrutinee` symbol against `tree` pattern */ private def patternPlan(scrutinee: Symbol, tree: Tree, onSuccess: Plan): Plan = { + extension (tree: Tree) def avoidPatBoundType(): Type = + tree.tpe.widen match + case tref: TypeRef if tref.symbol.isPatternBound => + defn.AnyType + case _ => + tree.tpe + /** Plan for matching `selectors` against argument patterns `args` */ def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan = { /* For a case with arguments that have some test on them such as @@ -243,7 +251,7 @@ object PatternMatcher { */ def matchArgsSelectorsPlan(selectors: List[Tree], syms: List[Symbol]): Plan = selectors match { - case selector :: selectors1 => letAbstract(selector)(sym => matchArgsSelectorsPlan(selectors1, sym :: syms)) + case selector :: selectors1 => letAbstract(selector, selector.avoidPatBoundType())(sym => matchArgsSelectorsPlan(selectors1, sym :: syms)) case Nil => matchArgsPatternPlan(args, syms.reverse) } def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan = diff --git a/tests/run/i5077.check b/tests/run/i5077.check new file mode 100644 index 000000000000..71fb2bf10e1c --- /dev/null +++ b/tests/run/i5077.check @@ -0,0 +1,3 @@ +A String with length 4 +A String with length 4 +A String with length 4 diff --git a/tests/run/i5077.scala b/tests/run/i5077.scala new file mode 100644 index 000000000000..bf70cb0c0d19 --- /dev/null +++ b/tests/run/i5077.scala @@ -0,0 +1,33 @@ +trait Is[A] +case object IsInt extends Is[Int] +case object IsString extends Is[String] +case class C[A](is: Is[A], value: A) + +@main +def Test = { + val c_string: C[String] = C(IsString, "name") + val c_any: C[_] = c_string + val any: Any = c_string + + // Case 1: no error + // `IsInt.equals` might be overridden to match a value of `C[String]` + c_string match { + case C(IsInt, _) => println(s"An Int") // Can't possibly happen! + case C(IsString, s) => println(s"A String with length ${s.length}") + case _ => println("No match") + } + + // Case 2: Should match the second case and print the length of the string + c_any match { + case C(IsInt, i) if i < 10 => println(s"An Int less than 10") + case C(IsString, s) => println(s"A String with length ${s.length}") + case _ => println("No match") + } + + // Case 3: Same as above; should match the second case and print the length of the string + any match { + case C(IsInt, i) if i < 10 => println(s"An Int less than 10") + case C(IsString, s) => println(s"A String with length ${s.length}") + case _ => println("No match") + } +} \ No newline at end of file