Skip to content

Commit

Permalink
Don't reduce inline matches, when a guard cannot be checked at compile
Browse files Browse the repository at this point in the history
time

fixes #13570
  • Loading branch information
KacperFKorban committed Oct 1, 2021
1 parent aaac006 commit d60ce59
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
29 changes: 20 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
*/
type MatchRedux = Option[(List[MemberDef], Tree)]

/** Same as MatchRedux, but also includes a boolean
* that is true if the guard can be checked at compile time.
*/
type MatchReduxWithGuard = Option[(List[MemberDef], Tree, Boolean)]

/** Reduce an inline match
* @param mtch the match tree
* @param scrutinee the scrutinee expression, assumed to be pure, or
Expand Down Expand Up @@ -1423,7 +1428,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
val scrutineeSym = newSym(InlineScrutineeName.fresh(), Synthetic, scrutType).asTerm
val scrutineeBinding = normalizeBinding(ValDef(scrutineeSym, scrutinee))

def reduceCase(cdef: CaseDef): MatchRedux = {
def reduceCase(cdef: CaseDef): MatchReduxWithGuard = {
val caseBindingMap = new mutable.ListBuffer[(Symbol, MemberDef)]()

def substBindings(
Expand All @@ -1442,21 +1447,27 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
val gadtCtx = ctx.fresh.setFreshGADTBounds.addMode(Mode.GadtConstraintInference)
if (reducePattern(caseBindingMap, scrutineeSym.termRef, cdef.pat)(using gadtCtx)) {
val (caseBindings, from, to) = substBindings(caseBindingMap.toList, mutable.ListBuffer(), Nil, Nil)
val guardOK = cdef.guard.isEmpty || {
typer.typed(cdef.guard.subst(from, to), defn.BooleanType) match {
case ConstantValue(true) => true
case _ => false
val typedGuard = typer.typed(cdef.guard.subst(from, to), defn.BooleanType)
val (guardOK, canReduceGuard) =
if cdef.guard.isEmpty then (true, true)
else typer.typed(cdef.guard.subst(from, to), defn.BooleanType) match {
case ConstantValue(v: Boolean) => (v, true)
case _ => (false, false)
}
}
if (guardOK) Some((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to)))
else None
if guardOK then Some((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard))
else if canReduceGuard then None
else Some((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard))
}
else None
}

def recur(cases: List[CaseDef]): MatchRedux = cases match {
case Nil => None
case cdef :: cases1 => reduceCase(cdef) `orElse` recur(cases1)
case cdef :: cases1 =>
reduceCase(cdef) match
case None => recur(cases1)
case r @ Some((caseBindings, rhs, canReduceGuard)) if canReduceGuard => Some((caseBindings, rhs))
case _ => None
}

recur(cases)
Expand Down
9 changes: 9 additions & 0 deletions tests/neg/i13570.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Error: tests/neg/i13570.scala:8:5 -----------------------------------------------------------------------------------
8 | jim(Seq(1,2)) // error
| ^^^^^^^^^^^^^
| cannot reduce inline match with
| scrutinee: seq$proxy1 : (seq$proxy1 : Seq[Int])
| patterns : case s @ _:Seq[Int] if s.isEmpty
| case s @ _:Seq[Int]
| case _
| This location contains code that was inlined from i13570.scala:3
8 changes: 8 additions & 0 deletions tests/neg/i13570.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test:
inline def jim(seq: Seq[Int]) =
inline seq match
case s: Seq[Int] if s.isEmpty => println("seq is empty")
case s: Seq[Int] => println("seq is not empty")
case _ => println("somthing hinky happened")

jim(Seq(1,2)) // error

0 comments on commit d60ce59

Please sign in to comment.