Skip to content

Commit 432aaaa

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.
1 parent c26b8f0 commit 432aaaa

17 files changed

+439
-431
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ object NameKinds {
319319
val PatMatOName = new UniqueNameKind("o") // FIXME: explain what this is
320320
val PatMatCaseName = new UniqueNameKind("case")
321321
val PatMatMatchFailName = new UniqueNameKind("matchFail")
322+
val PatMatResultName = new UniqueNameKind("matchResult")
322323
val PatMatSelectorName = new UniqueNameKind("selector")
323324

324325
val LocalOptInlineLocalObj = new UniqueNameKind("ilo")

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/core/tasty/TastyFormat.scala

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Standard-Section: "ASTs" TopLevelStat*
9090
IF Length cond_Term then_Term else_Term
9191
MATCH Length sel_Term CaseDef*
9292
TRY Length expr_Term CaseDef* finalizer_Term?
93+
LABELED Length bind_Bind tpt_Term expr_Term
9394
RETURN Length meth_ASTRef expr_Term?
9495
REPEATED Length elem_Type elem_Term*
9596
SELECTouter Length levels_Nat qual_Term underlying_Type
@@ -244,7 +245,7 @@ Standard Section: "Comments" Comment*
244245
object TastyFormat {
245246

246247
final val header = Array(0x5C, 0xA1, 0xAB, 0x1F)
247-
val MajorVersion = 10
248+
val MajorVersion = 11
248249
val MinorVersion = 0
249250

250251
/** Tags used to serialize names */
@@ -390,46 +391,47 @@ object TastyFormat {
390391
final val IF = 141
391392
final val LAMBDA = 142
392393
final val MATCH = 143
393-
final val RETURN = 144
394-
final val TRY = 145
395-
final val INLINED = 146
396-
final val SELECTouter = 147
397-
final val REPEATED = 148
398-
final val BIND = 149
399-
final val ALTERNATIVE = 150
400-
final val UNAPPLY = 151
401-
final val ANNOTATEDtype = 152
402-
final val ANNOTATEDtpt = 153
403-
final val CASEDEF = 154
404-
final val TEMPLATE = 155
405-
final val SUPER = 156
406-
final val SUPERtype = 157
407-
final val REFINEDtype = 158
408-
final val REFINEDtpt = 159
409-
final val APPLIEDtype = 160
410-
final val APPLIEDtpt = 161
411-
final val TYPEBOUNDS = 162
412-
final val TYPEBOUNDStpt = 163
413-
final val ANDtype = 164
414-
final val ANDtpt = 165
415-
final val ORtype = 166
416-
final val ORtpt = 167
417-
final val POLYtype = 168
418-
final val TYPELAMBDAtype = 169
419-
final val LAMBDAtpt = 170
420-
final val PARAMtype = 171
421-
final val ANNOTATION = 172
422-
final val TERMREFin = 173
423-
final val TYPEREFin = 174
424-
final val OBJECTDEF = 175
425-
426-
// In binary: 101100EI
394+
final val LABELED = 144
395+
final val RETURN = 145
396+
final val TRY = 146
397+
final val INLINED = 147
398+
final val SELECTouter = 148
399+
final val REPEATED = 149
400+
final val BIND = 150
401+
final val ALTERNATIVE = 151
402+
final val UNAPPLY = 152
403+
final val ANNOTATEDtype = 153
404+
final val ANNOTATEDtpt = 154
405+
final val CASEDEF = 155
406+
final val TEMPLATE = 156
407+
final val SUPER = 157
408+
final val SUPERtype = 158
409+
final val REFINEDtype = 159
410+
final val REFINEDtpt = 160
411+
final val APPLIEDtype = 161
412+
final val APPLIEDtpt = 162
413+
final val TYPEBOUNDS = 163
414+
final val TYPEBOUNDStpt = 164
415+
final val ANDtype = 165
416+
final val ANDtpt = 166
417+
final val ORtype = 167
418+
final val ORtpt = 168
419+
final val POLYtype = 169
420+
final val TYPELAMBDAtype = 170
421+
final val LAMBDAtpt = 171
422+
final val PARAMtype = 172
423+
final val ANNOTATION = 173
424+
final val TERMREFin = 174
425+
final val TYPEREFin = 175
426+
final val OBJECTDEF = 176
427+
428+
// In binary: 101101EI
427429
// I = implicit method type
428430
// E = erased method type
429-
final val METHODtype = 176
430-
final val IMPLICITMETHODtype = 177
431-
final val ERASEDMETHODtype = 178
432-
final val ERASEDIMPLICITMETHODtype = 179
431+
final val METHODtype = 180
432+
final val IMPLICITMETHODtype = 181
433+
final val ERASEDMETHODtype = 182
434+
final val ERASEDIMPLICITMETHODtype = 183
433435

434436
final val UNTYPEDSPLICE = 199
435437

@@ -600,6 +602,7 @@ object TastyFormat {
600602
case IF => "IF"
601603
case LAMBDA => "LAMBDA"
602604
case MATCH => "MATCH"
605+
case LABELED => "LABELED"
603606
case RETURN => "RETURN"
604607
case INLINED => "INLINED"
605608
case SELECTouter => "SELECTouter"

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ class TreePickler(pickler: TastyPickler) {
426426
case CaseDef(pat, guard, rhs) =>
427427
writeByte(CASEDEF)
428428
withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) }
429+
case Labeled(bind, expr) =>
430+
writeByte(LABELED)
431+
withLength { pickleTree(bind); pickleTree(expr) }
429432
case Return(expr, from) =>
430433
writeByte(RETURN)
431434
withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) }

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,10 @@ class TreeUnpickler(reader: TastyReader,
10711071
Closure(Nil, meth, tpt)
10721072
case MATCH =>
10731073
Match(readTerm(), readCases(end))
1074+
case LABELED =>
1075+
val bind = readTerm().asInstanceOf[Bind]
1076+
val expr = readTerm()
1077+
Labeled(bind, expr)
10741078
case RETURN =>
10751079
val from = readSymRef()
10761080
val expr = ifBefore(end)(readTerm(), EmptyTree)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
352352
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
353353
case CaseDef(pat, guard, body) =>
354354
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
355+
case Labeled(bind, expr) =>
356+
changePrec(GlobalPrec) { toText(bind.name) ~ keywordStr("[") ~ toText(bind.symbol.info) ~ keywordStr("]: ") ~ toText(expr) }
355357
case Return(expr, from) =>
356-
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
358+
val sym = from.symbol
359+
if (sym.is(Label))
360+
changePrec(GlobalPrec) { keywordStr("return[") ~ toText(sym.name) ~ keywordStr("]") ~ optText(expr)(" " ~ _) }
361+
else
362+
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
357363
case Try(expr, cases, finalizer) =>
358364
changePrec(GlobalPrec) {
359365
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)