@@ -273,20 +273,34 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
273
273
* should not be changed to {val $x = f(); T($x)}(1) but to {val $x = f(); val $y = 1; T($x)($y)}
274
274
*/
275
275
private def needsLift (tree : Apply )(using Context ): Boolean =
276
- def isBooleanOperator (fun : Tree ) =
277
- // We don't want to lift a || getB(), to avoid calling getB if a is true.
278
- // Same idea with a && getB(): if a is false, getB shouldn't be called.
279
- val sym = fun.symbol
280
- sym.exists &&
276
+ inline def isShortCircuitedOp (sym : Symbol ) =
281
277
sym == defn.Boolean_&& || sym == defn.Boolean_||
282
278
279
+ inline def isCompilerIntrinsic (sym : Symbol ) =
280
+ sym == defn.StringContext_s ||
281
+ sym == defn.StringContext_f ||
282
+ sym == defn.StringContext_raw
283
+
284
+ def isUnliftableFun (fun : Tree ) =
285
+ /*
286
+ * We don't want to lift a || getB(), to avoid calling getB if a is true.
287
+ * Same idea with a && getB(): if a is false, getB shouldn't be called.
288
+ *
289
+ * On top of that, the `s`, `f` and `raw` string interpolators are special-cased
290
+ * by the compiler and will disappear in phase StringInterpolatorOpt, therefore
291
+ * they shouldn't be lifted.
292
+ */
293
+ val sym = fun.symbol
294
+ sym.exists && (isShortCircuitedOp(sym) || isCompilerIntrinsic(sym))
295
+ end
296
+
283
297
val fun = tree.fun
284
298
val nestedApplyNeedsLift = fun match
285
299
case a : Apply => needsLift(a)
286
300
case _ => false
287
301
288
302
nestedApplyNeedsLift ||
289
- ! isBooleanOperator (fun) && ! tree.args.isEmpty && ! tree.args.forall(LiftCoverage .noLift)
303
+ ! isUnliftableFun (fun) && ! tree.args.isEmpty && ! tree.args.forall(LiftCoverage .noLift)
290
304
291
305
/** Check if the body of a DefDef can be instrumented with instrumentBody. */
292
306
private def canInstrumentDefDef (tree : DefDef )(using Context ): Boolean =
0 commit comments