Skip to content

Commit 2a74482

Browse files
committed
Introduce Labeled blocks, and use them in PatternMatcher.
A Labeled block is an expression tree of the form label[T]: { expr } where `expr` must conform to the type `T`. In addition, within `expr` (but nowhere else), return from the label is allowed: return[label] e where `e` must conform to the type `T` as well. If execution of `expr` completes normally (rather than throwing an exception or returning, etc.), then the result of evaluating the `Labeled` block is the result of `expr`. If a `return[label] e` is reached, the execution of `expr` is interrupted, and the result of evaluating the `Labeled` block is the result of evaluating the argument `e`. Implementation-wise, a `Labeled` block is represented as a `Tree` with the shape: Labeled(Bind(labelName), expr) where the `Bind` nodes holds the definition of the label symbol. That symbol is a term symbol with the flag `Label` (but not `Method`, unlike symbols for label-defs) and whose `info` is the result type `T` of the labeled block. We use those new `Labeled` blocks in `PatternMatcher`, instead of label-defs. This is the first step towards completely removing label-defs from the compiler. This commit structurally fixes a few issues: * It fixes #1313 through the `mergeTests` optimization. * It fixes #4563 because Labeled blocks are erasure-friendly. * It does a big step towards fixing the upstream test t10387: the compiler can get to the back-end on that test, but it produces too much bytecode for a single JVM method. We do add a sister test t10387b which works because optimizations can kick in.
1 parent 994df27 commit 2a74482

19 files changed

+755
-410
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
6161
type If = tpd.If
6262
type ValDef = tpd.ValDef
6363
type Throw = tpd.Apply
64+
type Labeled = tpd.Labeled
6465
type Return = tpd.Return
6566
type Block = tpd.Block
6667
type Typed = tpd.Typed
@@ -193,6 +194,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
193194
implicit val LabelDefTag: ClassTag[LabelDef] = ClassTag[LabelDef](classOf[LabelDef])
194195
implicit val ValDefTag: ClassTag[ValDef] = ClassTag[ValDef](classOf[ValDef])
195196
implicit val ThrowTag: ClassTag[Throw] = ClassTag[Throw](classOf[Throw])
197+
implicit val LabeledTag: ClassTag[Labeled] = ClassTag[Labeled](classOf[Labeled])
196198
implicit val ReturnTag: ClassTag[Return] = ClassTag[Return](classOf[Return])
197199
implicit val LiteralTag: ClassTag[Literal] = ClassTag[Literal](classOf[Literal])
198200
implicit val BlockTag: ClassTag[Block] = ClassTag[Block](classOf[Block])
@@ -1076,8 +1078,14 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
10761078
def apply(s: Symbol): This = tpd.This(s.asClass)
10771079
}
10781080

1081+
object Labeled extends LabeledDeconstructor {
1082+
def _1: Bind = field.bind
1083+
def _2: Tree = field.expr
1084+
}
1085+
10791086
object Return extends ReturnDeconstructor {
1080-
def get = field.expr
1087+
def _1: Tree = field.expr
1088+
def _2: Symbol = if (field.from.symbol.isLabel) field.from.symbol else NoSymbol
10811089
}
10821090

10831091
object Ident extends IdentDeconstructor {

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,15 @@ object Trees {
524524
type ThisTree[-T >: Untyped] = CaseDef[T]
525525
}
526526

527+
/** label[tpt]: { expr } */
528+
case class Labeled[-T >: Untyped] private[ast] (bind: Bind[T], expr: Tree[T])
529+
extends NameTree[T] {
530+
type ThisTree[-T >: Untyped] = Labeled[T]
531+
def name: Name = bind.name
532+
}
533+
527534
/** return expr
528-
* where `from` refers to the method from which the return takes place
535+
* where `from` refers to the method or label from which the return takes place
529536
* After program transformations this is not necessarily the enclosing method, because
530537
* closures can intervene.
531538
*/
@@ -886,6 +893,7 @@ object Trees {
886893
type Closure = Trees.Closure[T]
887894
type Match = Trees.Match[T]
888895
type CaseDef = Trees.CaseDef[T]
896+
type Labeled = Trees.Labeled[T]
889897
type Return = Trees.Return[T]
890898
type Try = Trees.Try[T]
891899
type SeqLiteral = Trees.SeqLiteral[T]
@@ -1028,6 +1036,10 @@ object Trees {
10281036
case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree
10291037
case _ => finalize(tree, untpd.CaseDef(pat, guard, body))
10301038
}
1039+
def Labeled(tree: Tree)(bind: Bind, expr: Tree)(implicit ctx: Context): Labeled = tree match {
1040+
case tree: Labeled if (bind eq tree.bind) && (expr eq tree.expr) => tree
1041+
case _ => finalize(tree, untpd.Labeled(bind, expr))
1042+
}
10311043
def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = tree match {
10321044
case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree
10331045
case _ => finalize(tree, untpd.Return(expr, from))
@@ -1202,6 +1214,8 @@ object Trees {
12021214
cpy.Match(tree)(transform(selector), transformSub(cases))
12031215
case CaseDef(pat, guard, body) =>
12041216
cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body))
1217+
case Labeled(bind, expr) =>
1218+
cpy.Labeled(tree)(transformSub(bind), transform(expr))
12051219
case Return(expr, from) =>
12061220
cpy.Return(tree)(transform(expr), transformSub(from))
12071221
case Try(block, cases, finalizer) =>
@@ -1334,6 +1348,8 @@ object Trees {
13341348
this(this(x, selector), cases)
13351349
case CaseDef(pat, guard, body) =>
13361350
this(this(this(x, pat), guard), body)
1351+
case Labeled(bind, expr) =>
1352+
this(this(x, bind), expr)
13371353
case Return(expr, from) =>
13381354
this(this(x, expr), from)
13391355
case Try(block, handler, finalizer) =>

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
120120
def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
121121
ta.assignType(untpd.Match(selector, cases), cases)
122122

123+
def Labeled(bind: Bind, expr: Tree)(implicit ctx: Context): Labeled =
124+
ta.assignType(untpd.Labeled(bind, expr))
125+
126+
def Labeled(sym: TermSymbol, expr: Tree)(implicit ctx: Context): Labeled =
127+
Labeled(Bind(sym, EmptyTree), expr)
128+
123129
def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return =
124130
ta.assignType(untpd.Return(expr, from))
125131

@@ -594,6 +600,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
594600
}
595601
}
596602

603+
override def Labeled(tree: Tree)(bind: Bind, expr: Tree)(implicit ctx: Context): Labeled =
604+
ta.assignType(untpd.cpy.Labeled(tree)(bind, expr))
605+
597606
override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return =
598607
ta.assignType(untpd.cpy.Return(tree)(expr, from))
599608

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
276276
def Closure(env: List[Tree], meth: Tree, tpt: Tree): Closure = new Closure(env, meth, tpt)
277277
def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
278278
def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
279+
def Labeled(bind: Bind, expr: Tree): Labeled = new Labeled(bind, expr)
279280
def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
280281
def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer)
281282
def SeqLiteral(elems: List[Tree], elemtpt: Tree): SeqLiteral = new SeqLiteral(elems, elemtpt)

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,8 @@ object NameKinds {
314314

315315
/** Kinds of unique names generated by the pattern matcher */
316316
val PatMatStdBinderName = new UniqueNameKind("x")
317-
val PatMatPiName = new UniqueNameKind("pi") // FIXME: explain what this is
318-
val PatMatPName = new UniqueNameKind("p") // FIXME: explain what this is
319-
val PatMatOName = new UniqueNameKind("o") // FIXME: explain what this is
320-
val PatMatCaseName = new UniqueNameKind("case")
321-
val PatMatMatchFailName = new UniqueNameKind("matchFail")
322-
val PatMatSelectorName = new UniqueNameKind("selector")
317+
val PatMatAltsName = new UniqueNameKind("matchAlts")
318+
val PatMatResultName = new UniqueNameKind("matchResult")
323319

324320
val LocalOptInlineLocalObj = new UniqueNameKind("ilo")
325321

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ object TypeErasure {
178178
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
179179
else if (sym.isAbstractType) TypeAlias(WildcardType)
180180
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
181+
else if (sym.is(Label, butNot = Method)) erase.eraseResult(sym.info)(erasureCtx)
181182
else erase.eraseInfo(tp, sym)(erasureCtx) match {
182183
case einfo: MethodType =>
183184
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass))

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
372372
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
373373
case CaseDef(pat, guard, body) =>
374374
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
375+
case Labeled(bind, expr) =>
376+
changePrec(GlobalPrec) { toText(bind.name) ~ keywordStr("[") ~ toText(bind.symbol.info) ~ keywordStr("]: ") ~ toText(expr) }
375377
case Return(expr, from) =>
376-
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
378+
val sym = from.symbol
379+
if (sym.is(Label))
380+
changePrec(GlobalPrec) { keywordStr("return[") ~ toText(sym.name) ~ keywordStr("]") ~ optText(expr)(" " ~ _) }
381+
else
382+
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
377383
case Try(expr, cases, finalizer) =>
378384
changePrec(GlobalPrec) {
379385
keywordStr("try ") ~ toText(expr) ~ optText(cases)(keywordStr(" catch ") ~ _) ~ optText(finalizer)(keywordStr(" finally ") ~ _)

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ object MegaPhase {
6666
def prepareForClosure(tree: Closure)(implicit ctx: Context) = ctx
6767
def prepareForMatch(tree: Match)(implicit ctx: Context) = ctx
6868
def prepareForCaseDef(tree: CaseDef)(implicit ctx: Context) = ctx
69+
def prepareForLabeled(tree: Labeled)(implicit ctx: Context) = ctx
6970
def prepareForReturn(tree: Return)(implicit ctx: Context) = ctx
7071
def prepareForTry(tree: Try)(implicit ctx: Context) = ctx
7172
def prepareForSeqLiteral(tree: SeqLiteral)(implicit ctx: Context) = ctx
@@ -98,6 +99,7 @@ object MegaPhase {
9899
def transformClosure(tree: Closure)(implicit ctx: Context): Tree = tree
99100
def transformMatch(tree: Match)(implicit ctx: Context): Tree = tree
100101
def transformCaseDef(tree: CaseDef)(implicit ctx: Context): Tree = tree
102+
def transformLabeled(tree: Labeled)(implicit ctx: Context): Tree = tree
101103
def transformReturn(tree: Return)(implicit ctx: Context): Tree = tree
102104
def transformTry(tree: Try)(implicit ctx: Context): Tree = tree
103105
def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context): Tree = tree
@@ -165,6 +167,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
165167
case tree: ValDef => goValDef(tree, start)
166168
case tree: DefDef => goDefDef(tree, start)
167169
case tree: TypeDef => goTypeDef(tree, start)
170+
case tree: Labeled => goLabeled(tree, start)
168171
case tree: Bind => goBind(tree, start)
169172
case _ => goOther(tree, start)
170173
}
@@ -249,6 +252,11 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
249252
implicit val ctx = prepTypeDef(tree, start)(outerCtx)
250253
val rhs = transformTree(tree.rhs, start)(localContext)
251254
goTypeDef(cpy.TypeDef(tree)(tree.name, rhs), start)
255+
case tree: Labeled =>
256+
implicit val ctx = prepLabeled(tree, start)(outerCtx)
257+
val bind = transformTree(tree.bind, start).asInstanceOf[Bind]
258+
val expr = transformTree(tree.expr, start)
259+
goLabeled(cpy.Labeled(tree)(bind, expr), start)
252260
case tree: Bind =>
253261
implicit val ctx = prepBind(tree, start)(outerCtx)
254262
val body = transformTree(tree.body, start)
@@ -745,6 +753,21 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
745753
}
746754
}
747755

756+
def prepLabeled(tree: Labeled, start: Int)(implicit ctx: Context): Context = {
757+
val phase = nxReturnPrepPhase(start)
758+
if (phase == null) ctx
759+
else prepLabeled(tree, phase.idxInGroup + 1)(phase.prepareForLabeled(tree))
760+
}
761+
762+
def goLabeled(tree: Labeled, start: Int)(implicit ctx: Context): Tree = {
763+
val phase = nxReturnTransPhase(start)
764+
if (phase == null) tree
765+
else phase.transformLabeled(tree)(ctx) match {
766+
case tree1: Labeled => goLabeled(tree1, phase.idxInGroup + 1)
767+
case tree1 => transformNode(tree1, phase.idxInGroup + 1)
768+
}
769+
}
770+
748771
def prepReturn(tree: Return, start: Int)(implicit ctx: Context): Context = {
749772
val phase = nxReturnPrepPhase(start)
750773
if (phase == null) ctx

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import collection.mutable
1111
object NonLocalReturns {
1212
import ast.tpd._
1313
def isNonLocalReturn(ret: Return)(implicit ctx: Context) =
14-
ret.from.symbol != ctx.owner.enclosingMethod || ctx.owner.is(Lazy)
14+
!ret.from.symbol.is(Label) && (ret.from.symbol != ctx.owner.enclosingMethod || ctx.owner.is(Lazy))
1515
}
1616

1717
/** Implement non-local returns using NonLocalReturnControl exceptions.

0 commit comments

Comments
 (0)