Skip to content

Commit 9187ba9

Browse files
committed
Treat narrowing pattern definitions as errors
1 parent 0e00e69 commit 9187ba9

File tree

5 files changed

+37
-4
lines changed

5 files changed

+37
-4
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ object desugar {
3232
*/
3333
val DerivingCompanion: Property.Key[SourcePosition] = new Property.Key
3434

35+
/** An attachment for match expressions generated from a PatDef */
36+
val PatDefMatch: Property.Key[Unit] = new Property.Key
37+
3538
/** Info of a variable in a pattern: The named tree and its type */
3639
private type VarInfo = (NameTree, Tree)
3740

@@ -948,7 +951,11 @@ object desugar {
948951
// - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
949952
val tupleOptimizable = forallResults(rhs, isMatchingTuple)
950953

951-
def rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
954+
def rhsUnchecked = {
955+
val rhs1 = makeAnnotated("scala.unchecked", rhs)
956+
rhs1.pushAttachment(PatDefMatch, ())
957+
rhs1
958+
}
952959
val vars =
953960
if (tupleOptimizable) // include `_`
954961
pat match {

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import ProtoTypes._
1616
import Scopes._
1717
import CheckRealizable._
1818
import ErrorReporting.errorTree
19+
import rewrites.Rewrites.patch
20+
import util.Spans.Span
1921

2022
import util.SourcePosition
2123
import transform.SymUtils._
@@ -594,6 +596,25 @@ trait Checking {
594596
ctx.error(ex"$cls cannot be instantiated since it${rstatus.msg}", pos)
595597
}
596598

599+
/** Check that pattern definition is either marked @unchecked or has a right
600+
* hand side with a type that conforms to the pattern's type.
601+
*/
602+
def checkPatDefMatch(tree: Tree, pt: Type)(implicit ctx: Context): Unit = tree match {
603+
case Match(_, CaseDef(pat, _, _) :: _)
604+
if !pat.tpe.widen.hasAnnotation(defn.UncheckedAnnot) && !(pt <:< pat.tpe) =>
605+
val pt1 = pt match {
606+
case AnnotatedType(pt1, annot) if annot.matches(defn.UncheckedAnnot) => pt1
607+
case _ => pt
608+
}
609+
ctx.errorOrMigrationWarning(
610+
ex"""pattern's type ${pat.tpe.widen} is more specialized than the right hand side expression's type $pt1
611+
|
612+
|If the narrowing is intentional, this can be communicated by writing `: @unchecked` after the pattern.${err.rewriteNotice}""",
613+
pat.sourcePos)
614+
if (ctx.scala2Mode) patch(Span(pat.span.end), ": @unchecked")
615+
case _ =>
616+
}
617+
597618
/** Check that `path` is a legal prefix for an import or export clause */
598619
def checkLegalImportPath(path: Tree)(implicit ctx: Context): Unit = {
599620
checkStable(path.tpe, path.sourcePos)

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ object ErrorReporting {
157157
}
158158
"""\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init))
159159
}
160+
161+
def rewriteNotice: String =
162+
if (ctx.scala2Mode) "\nThis patch can be inserted automatically under -rewrite."
163+
else ""
160164
}
161165

162166
def err(implicit ctx: Context): Errors = new Errors

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,10 @@ class Typer extends Namer
10361036
if (tree.isInline) checkInInlineContext("inline match", tree.posd)
10371037
val sel1 = typedExpr(tree.selector)
10381038
val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.span).widen
1039-
typedMatchFinish(tree, sel1, selType, tree.cases, pt)
1039+
val res = typedMatchFinish(tree, sel1, selType, tree.cases, pt)
1040+
if (tree.selector.removeAttachment(desugar.PatDefMatch).isDefined)
1041+
checkPatDefMatch(res, sel1.tpe)
1042+
res
10401043
}
10411044
}
10421045

tests/run/unchecked-patterns.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,4 @@ object Test extends App {
33
val (y1: Some[Int] @unchecked) = Some(1): Option[Int]
44

55
val a :: as: @unchecked = List(1, 2, 3)
6-
7-
86
}

0 commit comments

Comments
 (0)