Skip to content

Commit e02c9e1

Browse files
committed
Beta-reduce directly applied PolymorphicFunction
1 parent a30e6d4 commit e02c9e1

File tree

6 files changed

+73
-19
lines changed

6 files changed

+73
-19
lines changed

compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,13 @@ class InlineReducer(inliner: Inliner)(using Context):
165165
*/
166166
def betaReduce(tree: Tree)(using Context): Tree = tree match {
167167
case Apply(Select(cl, nme.apply), args) if defn.isFunctionType(cl.tpe) =>
168-
val bindingsBuf = new mutable.ListBuffer[ValDef]
168+
val bindingsBuf = new mutable.ListBuffer[DefTree]
169169
def recur(cl: Tree): Option[Tree] = cl match
170170
case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol =>
171171
ddef.tpe.widen match
172172
case mt: MethodType if ddef.paramss.head.length == args.length =>
173-
Some(BetaReduce.reduceApplication(ddef, args, bindingsBuf))
173+
// TODO beta reduce PolyType
174+
Some(BetaReduce.reduceApplication(ddef, List(args), bindingsBuf))
174175
case _ => None
175176
case Block(stats, expr) if stats.forall(isPureBinding) =>
176177
recur(expr).map(cpy.Block(cl)(stats, _))

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

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,49 +40,74 @@ class BetaReduce extends MiniPhase:
4040

4141
override def transformApply(app: Apply)(using Context): Tree = app.fun match
4242
case Select(fn, nme.apply) if defn.isFunctionType(fn.tpe) =>
43-
val app1 = BetaReduce(app, fn, app.args)
43+
val app1 = BetaReduce(app, fn, List(app.args))
44+
if app1 ne app then report.log(i"beta reduce $app -> $app1")
45+
app1
46+
case TypeApply(Select(fn, nme.apply), targs) if fn.tpe.typeSymbol eq defn.PolyFunctionClass =>
47+
val app1 = BetaReduce(app, fn, List(targs, app.args))
4448
if app1 ne app then report.log(i"beta reduce $app -> $app1")
4549
app1
4650
case _ =>
4751
app
4852

49-
5053
object BetaReduce:
5154
import ast.tpd._
5255

5356
val name: String = "betaReduce"
5457
val description: String = "reduce closure applications"
5558

5659
/** Beta-reduces a call to `fn` with arguments `argSyms` or returns `tree` */
57-
def apply(original: Tree, fn: Tree, args: List[Tree])(using Context): Tree =
60+
def apply(original: Tree, fn: Tree, argss: List[List[Tree]])(using Context): Tree =
5861
fn match
5962
case Typed(expr, _) =>
60-
BetaReduce(original, expr, args)
63+
BetaReduce(original, expr, argss)
6164
case Block((anonFun: DefDef) :: Nil, closure: Closure) =>
62-
BetaReduce(anonFun, args)
65+
BetaReduce(anonFun, argss)
66+
case Block((TypeDef(_, template: Template)) :: Nil, Typed(Apply(Select(New(_), _), _), _)) if template.constr.rhs.isEmpty =>
67+
template.body match
68+
case (anonFun: DefDef) :: Nil =>
69+
BetaReduce(anonFun, argss)
70+
case _ =>
71+
original
6372
case Block(stats, expr) =>
64-
val tree = BetaReduce(original, expr, args)
73+
val tree = BetaReduce(original, expr, argss)
6574
if tree eq original then original
6675
else cpy.Block(fn)(stats, tree)
6776
case Inlined(call, bindings, expr) =>
68-
val tree = BetaReduce(original, expr, args)
77+
val tree = BetaReduce(original, expr, argss)
6978
if tree eq original then original
7079
else cpy.Inlined(fn)(call, bindings, tree)
7180
case _ =>
7281
original
7382
end apply
7483

7584
/** Beta-reduces a call to `ddef` with arguments `args` */
76-
def apply(ddef: DefDef, args: List[Tree])(using Context) =
77-
val bindings = new ListBuffer[ValDef]()
78-
val expansion1 = reduceApplication(ddef, args, bindings)
85+
def apply(ddef: DefDef, argss: List[List[Tree]])(using Context) =
86+
val bindings = new ListBuffer[DefTree]()
87+
val expansion1 = reduceApplication(ddef, argss, bindings)
7988
val bindings1 = bindings.result()
8089
seq(bindings1, expansion1)
8190

8291
/** Beta-reduces a call to `ddef` with arguments `args` and registers new bindings */
83-
def reduceApplication(ddef: DefDef, args: List[Tree], bindings: ListBuffer[ValDef])(using Context): Tree =
84-
val vparams = ddef.termParamss.iterator.flatten.toList
92+
def reduceApplication(ddef: DefDef, argss: List[List[Tree]], bindings: ListBuffer[DefTree])(using Context): Tree =
93+
assert(argss.size == 1 || argss.size == 2)
94+
val targs = if argss.size == 2 then argss.head else Nil
95+
val args = argss.last
96+
val tparams = ddef.leadingTypeParams
97+
val vparams = ddef.termParamss.flatten
98+
assert(targs.hasSameLengthAs(tparams))
8599
assert(args.hasSameLengthAs(vparams))
100+
101+
val targSyms =
102+
for (targ, tparam) <- targs.zip(tparams) yield
103+
targ.tpe.dealias match
104+
case ref @ TypeRef(NoPrefix, _) =>
105+
ref.symbol
106+
case _ =>
107+
val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, targ.tpe, coord = targ.span)).withSpan(targ.span)
108+
bindings += binding
109+
binding.symbol
110+
86111
val argSyms =
87112
for (arg, param) <- args.zip(vparams) yield
88113
arg.tpe.dealias match
@@ -99,8 +124,8 @@ object BetaReduce:
99124
val expansion = TreeTypeMap(
100125
oldOwners = ddef.symbol :: Nil,
101126
newOwners = ctx.owner :: Nil,
102-
substFrom = vparams.map(_.symbol),
103-
substTo = argSyms
127+
substFrom = (tparams ::: vparams).map(_.symbol),
128+
substTo = targSyms ::: argSyms
104129
).transform(ddef.rhs)
105130

106131
val expansion1 = new TreeMap {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ class InlinePatterns extends MiniPhase:
5151
case Apply(App(fn, argss), args) => (fn, argss :+ args)
5252
case _ => (app, Nil)
5353

54+
// TODO merge with BetaReduce.scala
5455
private def betaReduce(tree: Apply, fn: Tree, name: Name, args: List[Tree])(using Context): Tree =
5556
fn match
5657
case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty =>
5758
template.body match
58-
case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, args)
59+
case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, List(args))
5960
case _ => tree
6061
case _ => tree
6162

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ object PickleQuotes {
322322
}
323323
val Block(List(ddef: DefDef), _) = splice: @unchecked
324324
// TODO: beta reduce inner closure? Or wait until BetaReduce phase?
325-
BetaReduce(ddef, spliceArgs).select(nme.apply).appliedTo(args(2).asInstance(quotesType))
325+
BetaReduce(ddef, List(spliceArgs)).select(nme.apply).appliedTo(args(2).asInstance(quotesType))
326326
}
327327
CaseDef(Literal(Constant(idx)), EmptyTree, rhs)
328328
}

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
362362
object Term extends TermModule:
363363
def betaReduce(tree: Term): Option[Term] =
364364
tree match
365+
// TODO support TypeApply. Would fix #15968.
365366
case app @ tpd.Apply(tpd.Select(fn, nme.apply), args) if dotc.core.Symbols.defn.isFunctionType(fn.tpe) =>
366-
val app1 = dotc.transform.BetaReduce(app, fn, args)
367+
val app1 = dotc.transform.BetaReduce(app, fn, List(args))
367368
if app1 eq app then None
368369
else Some(app1.withSpan(tree.span))
369370
case tpd.Block(Nil, expr) =>

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,32 @@ class InlineBytecodeTests extends DottyBytecodeTest {
582582
}
583583
}
584584

585+
@Test def beta_reduce_polymorphic_function = {
586+
val source = """class Test:
587+
| def test =
588+
| ([Z] => (arg: Z) => { val a: Z = arg; a }).apply[Int](2)
589+
""".stripMargin
590+
591+
checkBCode(source) { dir =>
592+
val clsIn = dir.lookupName("Test.class", directory = false).input
593+
val clsNode = loadClassNode(clsIn)
594+
595+
val fun = getMethod(clsNode, "test")
596+
val instructions = instructionsFromMethod(fun)
597+
val expected =
598+
List(
599+
Op(ICONST_2),
600+
VarOp(ISTORE, 1),
601+
VarOp(ILOAD, 1),
602+
Op(IRETURN)
603+
)
604+
605+
assert(instructions == expected,
606+
"`i was not properly beta-reduced in `test`\n" + diffInstructions(instructions, expected))
607+
608+
}
609+
}
610+
585611
@Test def i9456 = {
586612
val source = """class Foo {
587613
| def test: Int = inline2(inline1(2.+))

0 commit comments

Comments
 (0)