diff --git a/community-build/community-projects/scodec b/community-build/community-projects/scodec index 18047cac1feb..d59c0440b078 160000 --- a/community-build/community-projects/scodec +++ b/community-build/community-projects/scodec @@ -1 +1 @@ -Subproject commit 18047cac1febc840e03dee91a31183d64e632222 +Subproject commit d59c0440b078292dedff10b6743d6a086f468527 diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala index 64a91dfb17e6..bc08dbc36eea 100644 --- a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -84,6 +84,7 @@ object MatchTypeTrace: private def caseText(tp: Type)(using Context): String = tp match case tp: HKTypeLambda => caseText(tp.resultType) + case defn.MatchCase(any, body) if any eq defn.AnyType => i"case _ => $body" case defn.MatchCase(pat, body) => i"case $pat => $body" case _ => i"case $tp" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 63b6e9f8ac51..0063f6eb0af9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1824,7 +1824,7 @@ object Parsers { def typeDependingOn(location: Location): Tree = if location.inParens then typ() - else if location.inPattern then refinedType() + else if location.inPattern then rejectWildcardType(refinedType()) else infixType() /* ----------- EXPRESSIONS ------------------------------------------------ */ @@ -2610,15 +2610,21 @@ object Parsers { }) } - /** TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [semi] + /** TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] */ def typeCaseClause(): CaseDef = atSpan(in.offset) { val pat = inSepRegion(InCase) { accept(CASE) - infixType() + in.token match { + case USCORE if in.lookahead.isArrow => + val start = in.skipToken() + Ident(tpnme.WILDCARD).withSpan(Span(start, in.lastOffset, start)) + case _ => + rejectWildcardType(infixType()) + } } CaseDef(pat, EmptyTree, atSpan(accept(ARROW)) { - val t = typ() + val t = rejectWildcardType(typ()) if in.token == SEMI then in.nextToken() newLinesOptWhenFollowedBy(CASE) t diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6177faf6b6b0..39b7fc1a8668 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -485,6 +485,8 @@ class Typer extends Namer if ctx.mode.is(Mode.Pattern) then if name == nme.WILDCARD then return tree.withType(pt) + if name == tpnme.WILDCARD then + return tree.withType(defn.AnyType) if untpd.isVarPattern(tree) && name.isTermName then return typed(desugar.patternVar(tree), pt) else if ctx.mode.is(Mode.QuotedPattern) then @@ -1548,6 +1550,11 @@ class Typer extends Namer case defn.MatchCase(patternTp, _) => tpt.tpe frozen_=:= patternTp case _ => false } + case (id @ Ident(nme.WILDCARD), pt) => + pt match { + case defn.MatchCase(patternTp, _) => defn.AnyType frozen_=:= patternTp + case _ => false + } case _ => false } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 5cb03d5ab322..6ec1cbf6445f 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -289,7 +289,7 @@ CaseClauses ::= CaseClause { CaseClause } CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block CaseDef(pat, guard?, block) // block starts at => ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } -TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [semi] +TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) diff --git a/tests/neg/12261.scala b/tests/neg/12261.scala new file mode 100644 index 000000000000..70120671a05b --- /dev/null +++ b/tests/neg/12261.scala @@ -0,0 +1,15 @@ +type M0[X] = X match { + case ? => String // error: Unbound wildcard type +} + +type M1[X] = X match { + case Any => _ // error: Unbound wildcard type +} + +type M2[X] = X match { + case Any => ? // error: Unbound wildcard type +} + +val a = "" match { case _: _ => () } // error: Unbound wildcard type + +val b = try { } catch { case _: _ => () } // error: Unbound wildcard type diff --git a/tests/pos/9239.scala b/tests/pos/9239.scala index 8f3ddfbd1ced..c9c0ec268218 100644 --- a/tests/pos/9239.scala +++ b/tests/pos/9239.scala @@ -24,5 +24,5 @@ object ABug: N match case Zero => One case One => One - case ? => ![--[N]] × (N) + case _ => ![--[N]] × (N) case ? :: ? => ![--[N]] × (N) diff --git a/tests/pos/unify-wildcard-patterns.scala b/tests/pos/unify-wildcard-patterns.scala new file mode 100644 index 000000000000..9f18cebae1f5 --- /dev/null +++ b/tests/pos/unify-wildcard-patterns.scala @@ -0,0 +1,23 @@ +// `case _ => expr` in a match expression should be equivalant to +// `case _: Any => expr`. Likewise, in a match type, `case _ => T` +// should be equivalant to `case Any => T`. + +object Test0 { + type M[X] = X match { case String => Int case Any => String } + def m[X](x: X): M[X] = x match { case _: String => 1 case _: Any => "s" } +} + +object Test1 { + type M[X] = X match { case String => Int case Any => String } + def m[X](x: X): M[X] = x match { case _: String => 1 case _ => "s" } +} + +object Test2 { + type M[X] = X match { case String => Int case _ => String } + def m[X](x: X): M[X] = x match { case _: String => 1 case _: Any => "s" } +} + +object Test3 { + type M[X] = X match { case String => Int case _ => String } + def m[X](x: X): M[X] = x match { case _: String => 1 case _ => "s" } +}