Skip to content

Commit e6f4b74

Browse files
committed
Beta-reduce under blocks
`{ ...; (x: T) => f(x) }.apply(y)` is beta-reduced to `{ ...; val x = y; f(x) }`.
1 parent 24f018d commit e6f4b74

File tree

4 files changed

+61
-24
lines changed

4 files changed

+61
-24
lines changed

compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
302302
def betaReduce(tree: Term): Option[Term] =
303303
tree match
304304
case app @ tpd.Apply(tpd.Select(fn, nme.apply), args) if dotc.core.Symbols.defn.isFunctionType(fn.tpe) =>
305-
val app1 = dotc.transform.BetaReduce(app, fn, args)
306-
if app1 eq app then None
307-
else Some(app1.withSpan(tree.span))
305+
dotc.transform.BetaReduce.tryReduce(fn, args).map(_.withSpan(tree.span))
308306
case tpd.Block(Nil, expr) =>
309307
for e <- betaReduce(expr) yield tpd.cpy.Block(tree)(Nil, e)
310308
case tpd.Inlined(_, Nil, expr) =>

compiler/src/dotty/tools/dotc/transform/BetaReduce.scala

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ class BetaReduce extends MiniPhase:
3737

3838
override def transformApply(app: Apply)(using Context): Tree = app.fun match
3939
case Select(fn, nme.apply) if defn.isFunctionType(fn.tpe) =>
40-
val app1 = BetaReduce(app, fn, app.args)
41-
if app1 ne app then report.log(i"beta reduce $app -> $app1")
42-
app1
40+
BetaReduce.tryReduce(fn, app.args) match
41+
case Some(app1) =>
42+
report.log(i"beta reduce $app -> $app1")
43+
app1
44+
case _ =>
45+
app
4346
case _ =>
4447
app
4548

@@ -48,17 +51,22 @@ object BetaReduce:
4851
import ast.tpd._
4952

5053
/** Beta-reduces a call to `fn` with arguments `argSyms` or returns `tree` */
51-
def apply(tree: Apply, fn: Tree, args: List[Tree])(using Context): Tree =
54+
def tryReduce(fn: Tree, args: List[Tree])(using Context): Option[Tree] =
5255
fn match
53-
case Typed(expr, _) => BetaReduce(tree, expr, args)
54-
case Block(Nil, expr) => BetaReduce(tree, expr, args)
55-
case Inlined(_, Nil, expr) => BetaReduce(tree, expr, args)
56-
case Block((anonFun: DefDef) :: Nil, closure: Closure) => BetaReduce(anonFun, args)
57-
case _ => tree
58-
end apply
56+
case Typed(expr, _) =>
57+
tryReduce(expr, args)
58+
case Block((anonFun: DefDef) :: Nil, closure: Closure) =>
59+
Some(reduce(anonFun, args))
60+
case Block(stats, expr) =>
61+
tryReduce(expr, args).map(newExpr => cpy.Block(fn)(stats, newExpr))
62+
case Inlined(call, bindings, expr) =>
63+
tryReduce(expr, args).map(newExpr => cpy.Inlined(fn)(call, bindings, newExpr))
64+
case _ =>
65+
None
66+
end tryReduce
5967

6068
/** Beta-reduces a call to `ddef` with arguments `argSyms` */
61-
def apply(ddef: DefDef, args: List[Tree])(using Context) =
69+
def reduce(ddef: DefDef, args: List[Tree])(using Context) =
6270
val bindings = List.newBuilder[ValDef]
6371
val vparams = ddef.vparamss.iterator.flatten.toList
6472
assert(args.hasSameLengthAs(vparams))
@@ -90,4 +98,4 @@ object BetaReduce:
9098
bindings.result().filterNot(vdef => vdef.tpt.tpe.isInstanceOf[ConstantType] && isPureExpr(vdef.rhs))
9199

92100
seq(bindings1, expansion1)
93-
end apply
101+
end reduce

compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ class InlinePatterns extends MiniPhase:
5656
fn match
5757
case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty =>
5858
template.body match
59-
case List(ddef @ DefDef(`name`, _, _, _, _)) => BetaReduce(ddef, args)
59+
case List(ddef @ DefDef(`name`, _, _, _, _)) => BetaReduce.reduce(ddef, args)
6060
case _ => tree
6161
case _ => tree

compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -368,14 +368,14 @@ class InlineBytecodeTests extends DottyBytecodeTest {
368368
val instructions = instructionsFromMethod(fun)
369369
val expected =
370370
List(
371-
IntOp(BIPUSH, 10)
372-
, VarOp(ISTORE, 1)
373-
, VarOp(ILOAD, 1)
374-
, Op(ICONST_1)
375-
, Op(IADD)
376-
, VarOp(ISTORE, 1)
377-
, VarOp(ILOAD, 1)
378-
, Op(IRETURN)
371+
IntOp(BIPUSH, 10),
372+
VarOp(ISTORE, 1),
373+
VarOp(ILOAD, 1),
374+
Op(ICONST_1),
375+
Op(IADD),
376+
VarOp(ISTORE, 1),
377+
VarOp(ILOAD, 1),
378+
Op(IRETURN),
379379
)
380380
assert(instructions == expected,
381381
"`f` was not properly inlined in `fun`\n" + diffInstructions(instructions, expected))
@@ -524,4 +524,35 @@ class InlineBytecodeTests extends DottyBytecodeTest {
524524

525525
}
526526
}
527+
528+
@Test def beta_reduce_under_block = {
529+
val source = """class Test:
530+
| def test =
531+
| {
532+
| val a = 3
533+
| (i: Int) => i + a
534+
| }.apply(2)
535+
""".stripMargin
536+
537+
checkBCode(source) { dir =>
538+
val clsIn = dir.lookupName("Test.class", directory = false).input
539+
val clsNode = loadClassNode(clsIn)
540+
541+
val fun = getMethod(clsNode, "test")
542+
val instructions = instructionsFromMethod(fun)
543+
val expected =
544+
List(
545+
Op(ICONST_3),
546+
VarOp(ISTORE, 1),
547+
Op(ICONST_2),
548+
VarOp(ILOAD, 1),
549+
Op(IADD),
550+
Op(IRETURN),
551+
)
552+
553+
assert(instructions == expected,
554+
"`i was not properly beta-reduced in `test`\n" + diffInstructions(instructions, expected))
555+
556+
}
557+
}
527558
}

0 commit comments

Comments
 (0)