diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9273bef4b9f5..51aa4ef04d1e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1223,7 +1223,7 @@ object Types { * then the top-level union isn't widened. This is needed so that type inference can infer nullable types. */ def widenUnion(using Context): Type = widen match - case tp @ OrNull(tp1): OrType => + case tp @ OrNull(tp1) => // Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions. val tp1Widen = tp1.widenUnionWithoutNull if (tp1Widen.isRef(defn.AnyClass)) tp1Widen @@ -3158,7 +3158,7 @@ object Types { object OrNull { def apply(tp: Type)(using Context) = OrType(tp, defn.NullType, soft = false) - def unapply(tp: Type)(using Context): Option[Type] = + def unapply(tp: OrType)(using Context): Option[Type] = if (ctx.explicitNulls) { val tp1 = tp.stripNull() if tp1 ne tp then Some(tp1) else None diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6577c6a12ff3..ecedf9c45249 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2601,11 +2601,13 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= Pattern2 [Ascription] + /** Pattern1 ::= PatVar Ascription + * | SimpleLiteral Ascription + * | Pattern2 */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() - if in.token == COLON then + if (isVarPattern(p) || p.isInstanceOf[Number]) && in.token == COLON then in.nextToken() ascription(p, location) else p diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7ab102186a5e..3f43a697da48 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -425,13 +425,19 @@ class Typer extends Namer * (x: T | Null) => x.$asInstanceOf$[x.type & T] */ def toNotNullTermRef(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match - case ref @ OrNull(tpnn) : TermRef - if pt != AssignProto && // Ensure it is not the lhs of Assign + case ref: TermRef + if ctx.explicitNulls && + pt != AssignProto && // Ensure it is not the lhs of Assign ctx.notNullInfos.impliesNotNull(ref) && // If a reference is in the context, it is already trackable at the point we add it. // Hence, we don't use isTracked in the next line, because checking use out of order is enough. !ref.usedOutOfOrder => - tree.select(defn.Any_typeCast).appliedToType(AndType(ref, tpnn)) + val tp1 = ref.widenDealias + val tp2 = tp1.stripNull() + if tp1 ne tp2 then + tree.select(defn.Any_typeCast).appliedToType(AndType(ref, tp2)) + else + tree case _ => tree diff --git a/docs/docs/contributing/testing.md b/docs/docs/contributing/testing.md index c0fdca5f10dc..1a8fa5eb56d8 100644 --- a/docs/docs/contributing/testing.md +++ b/docs/docs/contributing/testing.md @@ -157,5 +157,5 @@ Expect files are used as regression tests to detect changes in the compiler. The test suite will create a new file if it detects any difference, which can be compared with the original expect file, or if the user wants to globally replace all expect files for semanticdb they can use -`dotty-compiler-bootstrapped/test:runMain dotty.tools.dotc.semanticdb.updateExpect`, and compare the changes via version +`scala3-compiler-bootstrapped/test:runMain dotty.tools.dotc.semanticdb.updateExpect`, and compare the changes via version control. diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 56648a81d4c2..40b50a991347 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -263,7 +263,9 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [nl] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) +Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) + | SimpleLiteral ‘:’ RefinedType Typed(pat, tpe) + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/tests/neg/i10994.scala b/tests/neg/i10994.scala new file mode 100644 index 000000000000..ce5cb2cf3df9 --- /dev/null +++ b/tests/neg/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () // error diff --git a/tests/neg/i8407.scala b/tests/neg/i8407.scala index 34d7fa20914c..de8e3c98a028 100644 --- a/tests/neg/i8407.scala +++ b/tests/neg/i8407.scala @@ -1,6 +1,6 @@ object Test: val xs = List(1, 2, 3, 4, 5) xs match { - case List(1, 2, xs1 @ xs2: _*) => println(xs2) // error // error + case List(1, 2, xs1 @ xs2: _*) => println(xs2) // error case _ => () } \ No newline at end of file diff --git a/tests/pos/patmat.scala b/tests/pos/patmat.scala index f34c5d98336d..46eb83f9142d 100644 --- a/tests/pos/patmat.scala +++ b/tests/pos/patmat.scala @@ -16,7 +16,7 @@ object Test { } (xs.length, xs) match { - case (0, Nil: List[Int]) => println("1") + case (0, Nil) => println("1") case (_, Nil) => println("2") case (0, _) => println("3") case (x, y) => println("4") @@ -46,9 +46,4 @@ object Test { case Some(s) => println(s) case None => println("nothing") } - - type IntPair = (Int, Int) - ??? match { - case (x, y): IntPair => x * y - } } diff --git a/tests/semanticdb/expect/ValPattern.expect.scala b/tests/semanticdb/expect/ValPattern.expect.scala index f89133f6ef5b..4369cc98fb65 100644 --- a/tests/semanticdb/expect/ValPattern.expect.scala +++ b/tests/semanticdb/expect/ValPattern.expect.scala @@ -6,7 +6,7 @@ class ValPattern/*<-example::ValPattern#*/ { val Some/*->scala::Some.*//*->scala::Some.unapply().*/(number1/*<-example::ValPattern#number1.*/) = Some/*->scala::Some.*//*->scala::Some.apply().*/(1) - val List/*->scala::package.List.*//*->scala::collection::SeqFactory#unapplySeq().*/(Some/*->scala::Some.*//*->scala::Some.unapply().*/(q1/*<-example::ValPattern#q1.*/), None/*->scala::None.*/: None/*->scala::None.*/.type, None/*->scala::None.*/) = ???/*->scala::Predef.`???`().*/ + val List/*->scala::package.List.*//*->scala::collection::SeqFactory#unapplySeq().*/(Some/*->scala::Some.*//*->scala::Some.unapply().*/(q1/*<-example::ValPattern#q1.*/), None/*->scala::None.*/) = ???/*->scala::Predef.`???`().*/ var (leftVar/*<-example::ValPattern#leftVar().*/, rightVar/*<-example::ValPattern#rightVar().*/) = (/*->scala::Tuple2.apply().*/1, 2) var Some/*->scala::Some.*//*->scala::Some.unapply().*/(number1Var/*<-example::ValPattern#number1Var().*/) = diff --git a/tests/semanticdb/expect/ValPattern.scala b/tests/semanticdb/expect/ValPattern.scala index b4e4ea3363c1..9d8923f0a45c 100644 --- a/tests/semanticdb/expect/ValPattern.scala +++ b/tests/semanticdb/expect/ValPattern.scala @@ -6,7 +6,7 @@ class ValPattern { val Some(number1) = Some(1) - val List(Some(q1), None: None.type, None) = ??? + val List(Some(q1), None) = ??? var (leftVar, rightVar) = (1, 2) var Some(number1Var) = diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 77bdc43c3182..7edb6d03aed8 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -2710,7 +2710,7 @@ Uri => ValPattern.scala Text => empty Language => Scala Symbols => 22 entries -Occurrences => 63 entries +Occurrences => 61 entries Symbols: example/ValPattern# => class ValPattern @@ -2754,9 +2754,7 @@ Occurrences: [8:15..8:15): -> scala/Some.unapply(). [8:16..8:18): q1 <- example/ValPattern#q1. [8:21..8:25): None -> scala/None. -[8:27..8:31): None -> scala/None. -[8:38..8:42): None -> scala/None. -[8:46..8:49): ??? -> scala/Predef.`???`(). +[8:29..8:32): ??? -> scala/Predef.`???`(). [10:7..10:14): leftVar <- example/ValPattern#leftVar(). [10:16..10:24): rightVar <- example/ValPattern#rightVar(). [10:29..10:29): -> scala/Tuple2.apply().