@@ -25,6 +25,7 @@ import Decorators._
2525import ErrorReporting .{err , errorType }
2626import config .Printers .typr
2727import NameKinds .DefaultGetterName
28+ import Applications .unapplyArgs
2829
2930import collection .mutable
3031import SymDenotations .{NoCompleter , NoDenotation }
@@ -596,23 +597,46 @@ trait Checking {
596597 ctx.error(ex " $cls cannot be instantiated since it ${rstatus.msg}" , pos)
597598 }
598599
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.
600+ /** Check that pattern `pat` is irrefutable for scrutinee tye `pt`.
601+ * This means `pat` is either marked @unchecked or `pt` conforms to the
602+ * pattern's type. If pattern is an UnApply, do the check recursively.
601603 */
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
604+ def checkIrrefutable (pat : Tree , pt : Type )(implicit ctx : Context ): Boolean = {
605+ patmatch.println(i " check irrefutable $pat: ${pat.tpe} against $pt" )
606+
607+ def check (pat : Tree , pt : Type ): Boolean = {
608+ if (pt <:< pat.tpe)
609+ true
610+ else {
611+ ctx.errorOrMigrationWarning(
612+ ex """ pattern's type ${pat.tpe} is more specialized than the right hand side expression's type ${pt.dropAnnot(defn.UncheckedAnnot )}
613+ |
614+ |If the narrowing is intentional, this can be communicated by writing `: @unchecked` after the full pattern. ${err.rewriteNotice}""" ,
615+ pat.sourcePos)
616+ false
617+ }
618+ }
619+
620+ ! ctx.settings.strict.value || // only in -strict mode for now since mitigations work only after this PR
621+ pat.tpe.widen.hasAnnotation(defn.UncheckedAnnot ) || {
622+ pat match {
623+ case Bind (_, pat1) =>
624+ checkIrrefutable(pat1, pt)
625+ case UnApply (fn, _, pats) =>
626+ check(pat, pt) && {
627+ val argPts = unapplyArgs(fn.tpe.finalResultType, fn, pats, pat.sourcePos)
628+ pats.corresponds(argPts)(checkIrrefutable)
629+ }
630+ case Alternative (pats) =>
631+ pats.forall(checkIrrefutable(_, pt))
632+ case Typed (arg, tpt) =>
633+ check(pat, pt) && checkIrrefutable(arg, pt)
634+ case Ident (nme.WILDCARD ) =>
635+ true
636+ case _ =>
637+ check(pat, pt)
608638 }
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 _ =>
639+ }
616640 }
617641
618642 /** Check that `path` is a legal prefix for an import or export clause */
0 commit comments