@@ -172,6 +172,19 @@ class InlineReducer(inliner: Inliner)(using Context):
172
172
173
173
val isImplicit = scrutinee.isEmpty
174
174
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
+
175
188
/** Try to match pattern `pat` against scrutinee reference `scrut`. If successful add
176
189
* bindings for variables bound in this pattern to `caseBindingMap`.
177
190
*/
@@ -184,10 +197,11 @@ class InlineReducer(inliner: Inliner)(using Context):
184
197
/** Create a binding of a pattern bound variable with matching part of
185
198
* scrutinee as RHS and type that corresponds to RHS.
186
199
*/
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)
189
204
caseBindingMap += ((sym, ValDef (copied, constToLiteral(rhs)).withSpan(sym.span)))
190
- }
191
205
192
206
def newTypeBinding (sym : TypeSymbol , alias : Type ): Unit = {
193
207
val copied = sym.copy(info = TypeAlias (alias), coord = sym.coord).asType
@@ -306,6 +320,7 @@ class InlineReducer(inliner: Inliner)(using Context):
306
320
case (Nil , Nil ) => true
307
321
case (pat :: pats1, selector :: selectors1) =>
308
322
val elem = newSym(InlineBinderName .fresh(), Synthetic , selector.tpe.widenInlineScrutinee).asTerm
323
+ adjustErased(elem, selector)
309
324
val rhs = constToLiteral(selector)
310
325
elem.defTree = rhs
311
326
caseBindingMap += ((NoSymbol , ValDef (elem, rhs).withSpan(elem.span)))
@@ -341,16 +356,18 @@ class InlineReducer(inliner: Inliner)(using Context):
341
356
val scrutineeSym = newSym(InlineScrutineeName .fresh(), Synthetic , scrutType).asTerm
342
357
val scrutineeBinding = normalizeBinding(ValDef (scrutineeSym, scrutinee))
343
358
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
352
369
scrutineeSym.setFlag(Erased )
353
-
370
+ if ! tpd.isPureExpr(scrutinee) then unusable += scrutineeSym
354
371
355
372
def reduceCase (cdef : CaseDef ): MatchReduxWithGuard = {
356
373
val caseBindingMap = new mutable.ListBuffer [(Symbol , MemberDef )]()
@@ -393,7 +410,25 @@ class InlineReducer(inliner: Inliner)(using Context):
393
410
case _ => None
394
411
}
395
412
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))
397
432
}
398
433
end InlineReducer
399
434
0 commit comments