Skip to content

Commit aa755f5

Browse files
committed
Move inline unapply logic from Typer to Inliner
1 parent 8b776bd commit aa755f5

File tree

2 files changed

+43
-26
lines changed

2 files changed

+43
-26
lines changed

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,48 @@ object Inliner {
124124
)
125125
}
126126

127+
/** Try to inline a pattern with an inline unapply method. Fail with error if the maximal
128+
* inline depth is exceeded.
129+
*
130+
* @param unapp The tree of the pattern to inline
131+
* @return An `Unapply` with a `fun` containing the inlined call to the unapply
132+
*/
133+
def inlinedUnapply(unapp: tpd.UnApply)(using ctx: Context): Tree = {
134+
// We cannot inline the unapply directly, since the pattern matcher relies on unapply applications
135+
// as signposts what to do. On the other hand, we can do the inlining only in typer, not afterwards.
136+
// So the trick is to create a "wrapper" unapply in an anonymous class that has the inlined unapply
137+
// as its right hand side. The call to the wrapper unapply serves as the signpost for pattern matching.
138+
// After pattern matching, the anonymous class is removed in phase InlinePatterns with a beta reduction step.
139+
//
140+
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
141+
// `{ class $anon { def unapply(t0: T0)(using t1: T1, t2: T2, ...): R = P.unapply(t0)(using t1, t2, ...) }; new $anon }.unapply`
142+
// and the call `P.unapply(x1, x2, ...)` is inlined.
143+
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
144+
// transforms the patterns into terms, the `inlinePatterns` phase removes this anonymous class by β-reducing
145+
// the call to the `unapply`.
146+
147+
val UnApply(fun, implicits, patterns) = unapp
148+
val sym = unapp.symbol
149+
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = sym.coord)
150+
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil, coord = sym.coord).entered
151+
152+
val targs = fun match
153+
case TypeApply(_, targs) => targs
154+
case _ => Nil
155+
val unapplyInfo = sym.info match
156+
case info: PolyType => info.instantiate(targs.map(_.tpe))
157+
case info => info
158+
159+
val unappplySym = ctx.newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
160+
val unapply = DefDef(unappplySym, argss =>
161+
inlineCall(fun.appliedToArgss(argss).withSpan(unapp.span))(ctx.withOwner(unappplySym))
162+
)
163+
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
164+
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
165+
val newFun = newUnapply.select(unappplySym).withSpan(unapp.span)
166+
cpy.UnApply(unapp)(newFun, implicits, patterns)
167+
}
168+
127169
/** For a retained inline method, another method that keeps track of
128170
* the body that is kept at runtime. For instance, an inline method
129171
*

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,32 +1250,7 @@ class Typer extends Namer
12501250
if (bounds != null) sym.info = bounds
12511251
}
12521252
b
1253-
case t: UnApply if t.symbol.is(Inline) =>
1254-
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
1255-
// `{ class $anon { def unapply(t0: T0)(using t1: T1, t2: T2, ...): R = P.unapply(t0)(using t1, t2, ...) }; new $anon }.unapply`
1256-
// and the call `P.unapply(x1, x2, ...)` is inlined.
1257-
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
1258-
// transforms the patterns into terms, the `inlinePatterns` phase removes this anonymous class by β-reducing
1259-
// the call to the `unapply`.
1260-
val sym = t.symbol
1261-
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = sym.coord)
1262-
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil, coord = sym.coord).entered
1263-
1264-
val targs = t.fun match
1265-
case TypeApply(_, targs) => targs
1266-
case _ => Nil
1267-
val unapplyInfo = sym.info match
1268-
case info: PolyType => info.instantiate(targs.map(_.tpe))
1269-
case info => info
1270-
1271-
val unappplySym = ctx.newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
1272-
val unapply = DefDef(unappplySym, argss =>
1273-
Inliner.inlineCall(t.fun.appliedToArgss(argss).withSpan(t.span))(ctx.withOwner(unappplySym))
1274-
)
1275-
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
1276-
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
1277-
val newFun = newUnapply.select(unappplySym).withSpan(t.span)
1278-
cpy.UnApply(t)(newFun, t.implicits, t.patterns)
1253+
case t: UnApply if t.symbol.is(Inline) => Inliner.inlinedUnapply(t)
12791254
case t => t
12801255
}
12811256
}

0 commit comments

Comments
 (0)