From c65573b11ba9ebdd4a1ffabbdb6d6093198ed60a Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 5 Dec 2024 17:13:24 +0100 Subject: [PATCH] Always treat underscores as type bounds inside patterns Always treat underscores as type bounds inside patterns, even when `ctx.settings.XkindProjector.value == "underscores"`. Fixes #14952 and #21400. [Cherry-picked 374cd4f7b5fd45c6d1ae96e7cbff7ca7f71010cf][modified] --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 14 +++++++++++--- tests/pos/14952.scala | 9 +++++++++ tests/pos/21400.scala | 7 +++++++ tests/pos/21400b.scala | 10 ++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/pos/14952.scala create mode 100644 tests/pos/21400.scala create mode 100644 tests/pos/21400b.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6267461185c2..2fa0745ab98e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -429,6 +429,14 @@ object Parsers { finally inEnum = saved } + private var inMatchPattern = false + private def withinMatchPattern[T](body: => T): T = { + val saved = inMatchPattern + inMatchPattern = true + try body + finally inMatchPattern = saved + } + private var staged = StageKind.None def withinStaged[T](kind: StageKind)(op: => T): T = { val saved = staged @@ -1857,7 +1865,7 @@ object Parsers { if isSimpleLiteral then SingletonTypeTree(simpleLiteral()) else if in.token == USCORE then - if ctx.settings.YkindProjector.value == "underscores" then + if ctx.settings.YkindProjector.value == "underscores" && !inMatchPattern then val start = in.skipToken() Ident(tpnme.USCOREkw).withSpan(Span(start, in.lastOffset, start)) else @@ -2883,7 +2891,7 @@ object Parsers { def caseClause(exprOnly: Boolean = false): CaseDef = atSpan(in.offset) { val (pat, grd) = inSepRegion(InCase) { accept(CASE) - (pattern(), guard()) + (withinMatchPattern(pattern()), guard()) } CaseDef(pat, grd, atSpan(accept(ARROW)) { if exprOnly then @@ -2907,7 +2915,7 @@ object Parsers { val start = in.skipToken() Ident(tpnme.WILDCARD).withSpan(Span(start, in.lastOffset, start)) case _ => - rejectWildcardType(infixType()) + withinMatchPattern(rejectWildcardType(infixType())) } } CaseDef(pat, EmptyTree, atSpan(accept(ARROW)) { diff --git a/tests/pos/14952.scala b/tests/pos/14952.scala new file mode 100644 index 000000000000..5e4fd1c9a5ee --- /dev/null +++ b/tests/pos/14952.scala @@ -0,0 +1,9 @@ +//> using options -Ykind-projector:underscores + +import Tuple.* + +type LiftP[F[_], T] <: Tuple = + T match { + case _ *: _ => F[Head[T]] *: LiftP[F, Tail[T]] + case _ => EmptyTuple + } diff --git a/tests/pos/21400.scala b/tests/pos/21400.scala new file mode 100644 index 000000000000..28a061883913 --- /dev/null +++ b/tests/pos/21400.scala @@ -0,0 +1,7 @@ +//> using options -Ykind-projector:underscores + +import scala.compiletime.ops.int.S + +type IndexOf[T <: Tuple, E] <: Int = T match + case E *: _ => 0 + case _ *: es => 1 // S[IndexOf[es, E]] diff --git a/tests/pos/21400b.scala b/tests/pos/21400b.scala new file mode 100644 index 000000000000..6426de9b0f94 --- /dev/null +++ b/tests/pos/21400b.scala @@ -0,0 +1,10 @@ +//> using options -Ykind-projector:underscores + +import scala.quoted.Type +import scala.quoted.Quotes + +def x[A](t: Type[A])(using Quotes): Boolean = t match + case '[_ *: _] => + true + case _ => + false