@@ -172,6 +172,19 @@ class InlineReducer(inliner: Inliner)(using Context):
172172
173173 val isImplicit = scrutinee.isEmpty
174174
175+ val unusable : util.EqHashSet [Symbol ] = util.EqHashSet ()
176+
177+ /** Adjust internaly generated value definitions;
178+ * - If the RHS refers to an erased symbol, mark the val as erased
179+ * - If the RHS refers to an erased symbol, mark the val as unsuable
180+ */
181+ def adjustErased (sym : TermSymbol , rhs : Tree ): Unit =
182+ rhs.foreachSubTree:
183+ case id : Ident if id.symbol.isErased =>
184+ sym.setFlag(Erased )
185+ if unusable.contains(id.symbol) then unusable += sym
186+ case _ =>
187+
175188 /** Try to match pattern `pat` against scrutinee reference `scrut`. If successful add
176189 * bindings for variables bound in this pattern to `caseBindingMap`.
177190 */
@@ -184,10 +197,11 @@ class InlineReducer(inliner: Inliner)(using Context):
184197 /** Create a binding of a pattern bound variable with matching part of
185198 * scrutinee as RHS and type that corresponds to RHS.
186199 */
187- def newTermBinding (sym : TermSymbol , rhs : Tree ): Unit = {
188- val copied = sym.copy(info = rhs.tpe.widenInlineScrutinee, coord = sym.coord, flags = sym.flags &~ Case ).asTerm
200+ def newTermBinding (sym : TermSymbol , rhs : Tree ): Unit =
201+ val copied = sym.copy(info = rhs.tpe.widenInlineScrutinee, coord = sym.coord,
202+ flags = sym.flags &~ Case ).asTerm
203+ adjustErased(copied, rhs)
189204 caseBindingMap += ((sym, ValDef (copied, constToLiteral(rhs)).withSpan(sym.span)))
190- }
191205
192206 def newTypeBinding (sym : TypeSymbol , alias : Type ): Unit = {
193207 val copied = sym.copy(info = TypeAlias (alias), coord = sym.coord).asType
@@ -306,6 +320,7 @@ class InlineReducer(inliner: Inliner)(using Context):
306320 case (Nil , Nil ) => true
307321 case (pat :: pats1, selector :: selectors1) =>
308322 val elem = newSym(InlineBinderName .fresh(), Synthetic , selector.tpe.widenInlineScrutinee).asTerm
323+ adjustErased(elem, selector)
309324 val rhs = constToLiteral(selector)
310325 elem.defTree = rhs
311326 caseBindingMap += ((NoSymbol , ValDef (elem, rhs).withSpan(elem.span)))
@@ -341,16 +356,18 @@ class InlineReducer(inliner: Inliner)(using Context):
341356 val scrutineeSym = newSym(InlineScrutineeName .fresh(), Synthetic , scrutType).asTerm
342357 val scrutineeBinding = normalizeBinding(ValDef (scrutineeSym, scrutinee))
343358
344- // If scrutinee has embedded `compiletime.erasedValue[T]` expressions, convert them to
345- // mark scrutineeSym as Erased. This means that the scrutinee cannot be referenced in
346- // the reduced term. It is NOT checked that scrutinee is a pure expression, since
347- // there is a special case in Erase that exempts the RHS of an erased scrutinee definition.
348- if scrutinee.existsSubTree:
349- case tree @ TypeApply (fn, args) => tree.symbol == defn.Compiletime_erasedValue
350- case _ => false
351- then
359+ // If scrutinee has embedded references to `compiletime.erasedValue` or to
360+ // other erased values, mark scrutineeSym as Erased. In addition, if scrutinee
361+ // is not a pure expression, mark scrutineeSym as unusable. The reason is that
362+ // scrutinee would then fail the tests in erasure that demand that the RHS of
363+ // an erased val is a pure expression. At the end of the inline match reduction
364+ // we throw out all unusable vals and check that the remaining code does not refer
365+ // to unusable symbols.
366+ // Note that compiletime.erasedValue is treated as erased but not pure, so scrutinees
367+ // containing references to it becomes unusable.
368+ if scrutinee.existsSubTree(_.symbol.isErased) then
352369 scrutineeSym.setFlag(Erased )
353-
370+ if ! tpd.isPureExpr(scrutinee) then unusable += scrutineeSym
354371
355372 def reduceCase (cdef : CaseDef ): MatchReduxWithGuard = {
356373 val caseBindingMap = new mutable.ListBuffer [(Symbol , MemberDef )]()
@@ -393,7 +410,25 @@ class InlineReducer(inliner: Inliner)(using Context):
393410 case _ => None
394411 }
395412
396- recur(cases)
413+ for (bindings, expr) <- recur(cases) yield
414+ // drop unusable vals and check that no referenes to unusable symbols remain
415+ val cleanupUnusable = new TreeMap :
416+ override def transform (tree : Tree )(using Context ): Tree =
417+ tree match
418+ case tree : ValDef if unusable.contains(tree.symbol) => EmptyTree
419+ case id : Ident if unusable.contains(id.symbol) =>
420+ report.error(
421+ em """ ${id.symbol} is unusable in ${ctx.owner} because it refers to an erased expression
422+ |in the selector of an inline match that reduces to
423+ |
424+ | ${Block (bindings, expr)}""" ,
425+ tree.srcPos)
426+ tree
427+ case _ => super .transform(tree)
428+
429+ val bindings1 = bindings.mapConserve(cleanupUnusable.transform).collect:
430+ case mdef : MemberDef => mdef
431+ (bindings1, cleanupUnusable.transform(expr))
397432 }
398433end InlineReducer
399434
0 commit comments