Skip to content

Commit

Permalink
Detect irrefutable quoted patterns
Browse files Browse the repository at this point in the history
Fixes #16649
  • Loading branch information
nicolasstucki committed Jan 16, 2023
1 parent ed5b119 commit a2869dc
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 3 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,9 @@ class Definitions {

@tu lazy val QuoteMatchingClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteMatching")
@tu lazy val QuoteMatching_ExprMatch: Symbol = QuoteMatchingClass.requiredMethod("ExprMatch")
@tu lazy val QuoteMatching_ExprMatchModule: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule")
@tu lazy val QuoteMatching_TypeMatch: Symbol = QuoteMatchingClass.requiredMethod("TypeMatch")
@tu lazy val QuoteMatching_TypeMatchModule: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule")

@tu lazy val ToExprModule: Symbol = requiredModule("scala.quoted.ToExpr")
@tu lazy val ToExprModule_BooleanToExpr: Symbol = ToExprModule.requiredMethod("BooleanToExpr")
Expand Down
21 changes: 21 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,27 @@ object SpaceEngine {
case funRef: TermRef => isIrrefutable(funRef, argLen)
case _: ErrorType => false
}

/** Is this an `'{..}` or `'[..]` irrefutable quoted patterns?
* @param unapp The unapply function tree
* @param implicits The implicits of the unapply
* @param pt The scrutinee type
*/
def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = {
implicits.headOption match
// pattern '{ $x: T }
case Some(tpd.Apply(tpd.Select(tpd.Quoted(tpd.TypeApply(fn, List(tpt))), nme.apply), _))
if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule)
&& fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) =>
pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)

// pattern '[T]
case Some(tpd.Apply(tpd.TypeApply(fn, List(tpt)), _))
if unapp.symbol.owner.eq(defn.QuoteMatching_TypeMatchModule) =>
pt =:= defn.QuotedTypeClass.typeRef.appliedTo(tpt.tpe)

case _ => false
}
}

/** Scala implementation of space logic */
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import NameOps._
import SymDenotations.{NoCompleter, NoDenotation}
import Applications.unapplyArgs
import Inferencing.isFullyDefined
import transform.patmat.SpaceEngine.isIrrefutable
import transform.patmat.SpaceEngine.{isIrrefutable, isIrrefutableQuotedPattern}
import config.Feature
import config.Feature.sourceVersion
import config.SourceVersion._
Expand Down Expand Up @@ -888,9 +888,9 @@ trait Checking {
pat match
case Bind(_, pat1) =>
recur(pat1, pt)
case UnApply(fn, _, pats) =>
case UnApply(fn, implicits, pats) =>
check(pat, pt) &&
(isIrrefutable(fn, pats.length) || fail(pat, pt, Reason.RefutableExtractor)) && {
(isIrrefutable(fn, pats.length) || isIrrefutableQuotedPattern(fn, implicits, pt) || fail(pat, pt, Reason.RefutableExtractor)) && {
val argPts = unapplyArgs(fn.tpe.widen.finalResultType, fn, pats, pat.srcPos)
pats.corresponds(argPts)(recur)
}
Expand Down
7 changes: 7 additions & 0 deletions tests/pos-special/fatal-warnings/i16649-irrefutable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import quoted.*

def foo(using Quotes)(x: Expr[Int]) =
val '{ $y } = x
val '{ $a: Any } = x
val '{ $b: Int } = x
val '[List[Int]] = Type.of[List[Int]]

0 comments on commit a2869dc

Please sign in to comment.