Skip to content

Commit caff137

Browse files
committed
Remove placeholer inserted by inlined patterns
1 parent 4f7f34c commit caff137

File tree

5 files changed

+87
-3
lines changed

5 files changed

+87
-3
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Compiler {
7979
new StringInterpolatorOpt, // Optimizes raw and s string interpolators by rewriting them to string concatentations
8080
new CrossCastAnd) :: // Normalize selections involving intersection types.
8181
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
82+
new InlinePatterns, // Remove placeholders of inlined patterns
8283
new VCInlineMethods, // Inlines calls to value class methods
8384
new SeqLiterals, // Express vararg arguments as arrays
8485
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package dotty.tools
2+
package dotc
3+
package transform
4+
5+
import core._
6+
import MegaPhase._
7+
import Symbols._, Contexts._, Types._, Decorators._
8+
import StdNames.nme
9+
import NameOps._
10+
import Names._
11+
import ast.Trees._
12+
import ast.TreeTypeMap
13+
14+
/** Rewrite an application
15+
*
16+
* {new { def unapply(x0: X0)(x1: X1,..., xn: Xn) = b }}.unapply(y0)(y1, ..., yn)
17+
*
18+
* where
19+
*
20+
* - the method is `unapply` or `unapplySeq`
21+
* - the method does not have type parameters
22+
*
23+
* to
24+
*
25+
* [xi := yi]b
26+
*
27+
* This removes placeholders added by inline `unapply`/`unapplySeq` patterns.
28+
*/
29+
class InlinePatterns extends MiniPhase:
30+
import ast.tpd._
31+
32+
def phaseName: String = "inlinePatterns"
33+
34+
override def runsAfterGroupsOf: Set[String] = Set(PatternMatcher.name)
35+
36+
override def transformApply(app: Apply)(using ctx: Context): Tree =
37+
if app.symbol.name.isUnapplyName && !app.tpe.isInstanceOf[MethodicType] then
38+
app match
39+
case App(Select(fn, name), argss) =>
40+
val app1 = betaReduce(app, fn, name, argss.flatten)
41+
if app1 ne app then ctx.log(i"beta reduce $app -> $app1")
42+
app1
43+
case _ =>
44+
app
45+
else app
46+
47+
private object App:
48+
def unapply(app: Tree): (Tree, List[List[Tree]]) =
49+
app match
50+
case Apply(App(fn, argss), args) => (fn, argss :+ args)
51+
case _ => (app, Nil)
52+
53+
private def betaReduce(tree: Apply, fn: Tree, name: Name, args: List[Tree])(using ctx: Context): Tree =
54+
fn match
55+
case Block(Nil, expr) => betaReduce(tree, expr, name, args)
56+
case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty =>
57+
template.body match
58+
case List(ddef @ DefDef(`name`, _, _, _, _)) =>
59+
val bindings = List.newBuilder[ValDef]
60+
val vparams = ddef.vparamss.flatten
61+
val argSyms =
62+
for (arg, param) <- args.zip(vparams) yield
63+
arg.tpe.dealias match
64+
case ref @ TermRef(NoPrefix, _) if isPurePath(arg) =>
65+
ref.symbol
66+
case _ =>
67+
val binding = SyntheticValDef(param.name, arg)
68+
bindings += binding
69+
binding.symbol
70+
seq(
71+
bindings.result(),
72+
TreeTypeMap(
73+
oldOwners = ddef.symbol :: Nil,
74+
newOwners = ctx.owner :: Nil,
75+
substFrom = vparams.map(_.symbol),
76+
substTo = argSyms).transform(ddef.rhs)
77+
)
78+
79+
case _ => tree
80+
case _ => tree

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,11 @@ class Typer extends Namer
12521252
b
12531253
case t: UnApply if t.symbol.is(Inline) =>
12541254
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
1255-
// `{ class $anon { def unapply(x1: T1, x2: T2, ...): R = P.unapply(x1, x2, ...) }; new $anon }.unapply`
1255+
// `{ class $anon { def unapply(t0: T0)(using t1: T1, t2: T2, ...): R = P.unapply(t0)(using t1, t2, ...) }; new $anon }.unapply`
12561256
// 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`.
12571260
val sym = t.symbol
12581261
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = sym.coord)
12591262
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil, coord = sym.coord).entered

tests/pos/i8530.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object MySeqUnapply:
1414
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x)
1515

1616
object MyWhiteboxUnapply:
17-
inline def unapply(x: Int) <: Option[Any] = Some(x)
17+
transparent inline def unapply(x: Int): Option[Any] = Some(x)
1818

1919
def test: Unit =
2020
val x = 5 match

tests/run/i8530.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object MySeqUnapply:
1111
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x + 1)
1212

1313
object MyWhiteboxUnapply:
14-
inline def unapply(x: Int) <: Option[Any] = Some(x)
14+
transparent inline def unapply(x: Int): Option[Any] = Some(x)
1515

1616

1717
@main def Test =

0 commit comments

Comments
 (0)