Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect irrefutable quoted patterns #16674

Merged
merged 1 commit into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]]