Skip to content

Commit 229fdaa

Browse files
committed
Handle binding of beta reduced inlined lambdas
Handle all inline beta-reduction in the InlineReducer. All these applications will contain `Inlined` nodes that need to be handled without changing the nestedness of expressions in inlining scopes. Fixes #16374
1 parent b4f8eef commit 229fdaa

File tree

5 files changed

+58
-31
lines changed

5 files changed

+58
-31
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

-2
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
743743
Some(meth)
744744
case Block(Nil, expr) =>
745745
unapply(expr)
746-
case Inlined(_, bindings, expr) if bindings.forall(isPureBinding) =>
747-
unapply(expr)
748746
case _ =>
749747
None
750748
}

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

+38-27
Original file line numberDiff line numberDiff line change
@@ -158,35 +158,46 @@ class InlineReducer(inliner: Inliner)(using Context):
158158
*
159159
* where `def` is used for call-by-name parameters. However, we shortcut any NoPrefix
160160
* refs among the ei's directly without creating an intermediate binding.
161+
*
162+
* This variant of beta-reduction preserves the integrity of `Inlined` tree nodes.
161163
*/
162164
def betaReduce(tree: Tree)(using Context): Tree = tree match {
163-
case Apply(Select(cl @ closureDef(ddef), nme.apply), args) if defn.isFunctionType(cl.tpe) =>
164-
// closureDef also returns a result for closures wrapped in Inlined nodes.
165-
// These need to be preserved.
166-
def recur(cl: Tree): Tree = cl match
167-
case Inlined(call, bindings, expr) =>
168-
cpy.Inlined(cl)(call, bindings, recur(expr))
169-
case _ => ddef.tpe.widen match
170-
case mt: MethodType if ddef.paramss.head.length == args.length =>
171-
val bindingsBuf = new DefBuffer
172-
val argSyms = mt.paramNames.lazyZip(mt.paramInfos).lazyZip(args).map { (name, paramtp, arg) =>
173-
arg.tpe.dealias match {
174-
case ref @ TermRef(NoPrefix, _) => ref.symbol
175-
case _ =>
176-
paramBindingDef(name, paramtp, arg, bindingsBuf)(
177-
using ctx.withSource(cl.source)
178-
).symbol
165+
case Apply(Select(cl, nme.apply), args) if defn.isFunctionType(cl.tpe) =>
166+
val bindingsBuf = new DefBuffer
167+
def recur(cl: Tree): Option[Tree] = cl match
168+
case Inlined(call, bindings, expr) if bindings.forall(isPureBinding) =>
169+
recur(expr).map(cpy.Inlined(cl)(call, bindings, _))
170+
case Block(Nil, expr) =>
171+
recur(expr).map(cpy.Block(cl)(Nil, _))
172+
case Typed(expr, tpt) =>
173+
recur(expr)
174+
case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol =>
175+
ddef.tpe.widen match
176+
case mt: MethodType if ddef.paramss.head.length == args.length =>
177+
val argSyms = mt.paramNames.lazyZip(mt.paramInfos).lazyZip(args).map { (name, paramtp, arg) =>
178+
arg.tpe.dealias match {
179+
case ref @ TermRef(NoPrefix, _) => ref.symbol
180+
case _ =>
181+
paramBindingDef(name, paramtp, arg, bindingsBuf)(
182+
using ctx.withSource(cl.source)
183+
).symbol
184+
}
179185
}
180-
}
181-
val expander = new TreeTypeMap(
182-
oldOwners = ddef.symbol :: Nil,
183-
newOwners = ctx.owner :: Nil,
184-
substFrom = ddef.paramss.head.map(_.symbol),
185-
substTo = argSyms)
186-
Block(bindingsBuf.toList, expander.transform(ddef.rhs)).withSpan(tree.span)
187-
case _ => tree
188-
recur(cl)
189-
case _ => tree
186+
val expander = new TreeTypeMap(
187+
oldOwners = ddef.symbol :: Nil,
188+
newOwners = ctx.owner :: Nil,
189+
substFrom = ddef.paramss.head.map(_.symbol),
190+
substTo = argSyms)
191+
Some(expander.transform(ddef.rhs))
192+
case _ => None
193+
case _ => None
194+
recur(cl) match
195+
case Some(reduced) =>
196+
Block(bindingsBuf.toList, reduced).withSpan(tree.span)
197+
case None =>
198+
tree
199+
case _ =>
200+
tree
190201
}
191202

192203
/** The result type of reducing a match. It consists optionally of a list of bindings
@@ -281,7 +292,7 @@ class InlineReducer(inliner: Inliner)(using Context):
281292
// Test case is pos-macros/i15971
282293
val tptBinds = getBinds(Set.empty[TypeSymbol], tpt)
283294
val binds: Set[TypeSymbol] = pat match {
284-
case UnApply(TypeApply(_, tpts), _, _) =>
295+
case UnApply(TypeApply(_, tpts), _, _) =>
285296
getBinds(Set.empty[TypeSymbol], tpts) ++ tptBinds
286297
case _ => tptBinds
287298
}

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -600,10 +600,12 @@ class InlineBytecodeTests extends DottyBytecodeTest {
600600
val instructions = instructionsFromMethod(fun)
601601
val expected = // TODO room for constant folding
602602
List(
603-
Op(ICONST_1),
603+
Op(ICONST_2),
604604
VarOp(ISTORE, 1),
605+
Op(ICONST_1),
606+
VarOp(ISTORE, 2),
605607
Op(ICONST_2),
606-
VarOp(ILOAD, 1),
608+
VarOp(ILOAD, 2),
607609
Op(IADD),
608610
Op(ICONST_3),
609611
Op(IADD),

tests/pos/i16374a.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def method(using String): String = ???
2+
3+
inline def inlineMethod(inline op: String => Unit)(using String): Unit =
4+
println(op(method))
5+
6+
def test(using String) =
7+
inlineMethod(c => print(c))

tests/pos/i16374b.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
def method(using String): String = ???
2+
3+
inline def identity[T](inline x: T): T = x
4+
5+
inline def inlineMethod(inline op: String => Unit)(using String): Unit =
6+
println(identity(op)(method))
7+
8+
def test(using String) =
9+
inlineMethod(c => print(c))

0 commit comments

Comments
 (0)