Skip to content

Commit eed948f

Browse files
authored
Merge pull request #5392 from dotty-staging/add-inline-matches
Inline and implicit matches
2 parents a967a25 + 330f666 commit eed948f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1065
-254
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
399399
case Apply(fn, args) =>
400400
def isKnownPureOp(sym: Symbol) =
401401
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
402-
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)
403-
// A constant expression with pure arguments is pure.
404-
|| fn.symbol.isStable)
402+
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
403+
|| fn.symbol.isStable
404+
|| fn.symbol.isPrimaryConstructor && fn.symbol.owner.isNoInitsClass) // TODO: include in isStable?
405405
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
406406
else if (fn.symbol.is(Erased)) Pure
407407
else Impure

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@ object Trees {
495495
case class If[-T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])
496496
extends TermTree[T] {
497497
type ThisTree[-T >: Untyped] = If[T]
498+
def isInline = false
499+
}
500+
class InlineIf[T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])
501+
extends If(cond, thenp, elsep) {
502+
override def isInline = true
503+
override def toString = s"InlineIf($cond, $thenp, $elsep)"
498504
}
499505

500506
/** A closure with an environment and a reference to a method.
@@ -515,6 +521,12 @@ object Trees {
515521
case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])
516522
extends TermTree[T] {
517523
type ThisTree[-T >: Untyped] = Match[T]
524+
def isInline = false
525+
}
526+
class InlineMatch[T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])
527+
extends Match(selector, cases) {
528+
override def isInline = true
529+
override def toString = s"InlineMatch($selector, $cases)"
518530
}
519531

520532
/** case pat if guard => body; only appears as child of a Match */
@@ -901,8 +913,10 @@ object Trees {
901913
type Assign = Trees.Assign[T]
902914
type Block = Trees.Block[T]
903915
type If = Trees.If[T]
916+
type InlineIf = Trees.InlineIf[T]
904917
type Closure = Trees.Closure[T]
905918
type Match = Trees.Match[T]
919+
type InlineMatch = Trees.InlineMatch[T]
906920
type CaseDef = Trees.CaseDef[T]
907921
type Labeled = Trees.Labeled[T]
908922
type Return = Trees.Return[T]
@@ -1030,6 +1044,7 @@ object Trees {
10301044
}
10311045
def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = tree match {
10321046
case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree
1047+
case tree: InlineIf => finalize(tree, untpd.InlineIf(cond, thenp, elsep))
10331048
case _ => finalize(tree, untpd.If(cond, thenp, elsep))
10341049
}
10351050
def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = tree match {
@@ -1038,6 +1053,7 @@ object Trees {
10381053
}
10391054
def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = tree match {
10401055
case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree
1056+
case tree: InlineMatch => finalize(tree, untpd.InlineMatch(selector, cases))
10411057
case _ => finalize(tree, untpd.Match(selector, cases))
10421058
}
10431059
def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = tree match {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
8585
def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
8686
ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep)
8787

88+
def InlineIf(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
89+
ta.assignType(untpd.InlineIf(cond, thenp, elsep), thenp, elsep)
90+
8891
def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
8992
ta.assignType(untpd.Closure(env, meth, tpt), meth, tpt)
9093

@@ -121,6 +124,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
121124
def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
122125
ta.assignType(untpd.Match(selector, cases), selector, cases)
123126

127+
def InlineMatch(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
128+
ta.assignType(untpd.InlineMatch(selector, cases), selector, cases)
129+
124130
def Labeled(bind: Bind, expr: Tree)(implicit ctx: Context): Labeled =
125131
ta.assignType(untpd.Labeled(bind, expr))
126132

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
271271
def Assign(lhs: Tree, rhs: Tree): Assign = new Assign(lhs, rhs)
272272
def Block(stats: List[Tree], expr: Tree): Block = new Block(stats, expr)
273273
def If(cond: Tree, thenp: Tree, elsep: Tree): If = new If(cond, thenp, elsep)
274+
def InlineIf(cond: Tree, thenp: Tree, elsep: Tree): If = new InlineIf(cond, thenp, elsep)
274275
def Closure(env: List[Tree], meth: Tree, tpt: Tree): Closure = new Closure(env, meth, tpt)
275276
def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
277+
def InlineMatch(selector: Tree, cases: List[CaseDef]): Match = new InlineMatch(selector, cases)
276278
def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
277279
def Labeled(bind: Bind, expr: Tree): Labeled = new Labeled(bind, expr)
278280
def Return(expr: Tree, from: Tree): Return = new Return(expr, from)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ class Definitions {
338338
def NullType: TypeRef = NullClass.typeRef
339339
lazy val RuntimeNullModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.Null")
340340

341+
lazy val ImplicitScrutineeTypeSym =
342+
newSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered
343+
def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef
344+
345+
341346
lazy val ScalaPredefModuleRef: TermRef = ctx.requiredModuleRef("scala.Predef")
342347
def ScalaPredefModule(implicit ctx: Context): Symbol = ScalaPredefModuleRef.symbol
343348

@@ -1296,5 +1301,4 @@ class Definitions {
12961301
isInitialized = true
12971302
}
12981303
}
1299-
13001304
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,10 @@ object SymDenotations {
793793
def isSkolem: Boolean = name == nme.SKOLEM
794794

795795
def isInlineMethod(implicit ctx: Context): Boolean =
796-
is(InlineMethod, butNot = AccessorOrSynthetic)
796+
is(InlineMethod, butNot = AccessorOrSynthetic) &&
797+
name != nme.unapply // unapply methods do not count as inline methods
798+
// we need an inline flag on them only do that
799+
// reduceProjection gets access to their rhs
797800

798801
/** An erased value or an inline method, excluding @forceInline annotated methods.
799802
* The latter have to be kept around to get to parity with Scala.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ Standard-Section: "ASTs" TopLevelStat*
8888
BLOCK Length expr_Term Stat*
8989
INLINED Length expr_Term call_Term? ValOrDefDef*
9090
LAMBDA Length meth_Term target_Type?
91-
IF Length cond_Term then_Term else_Term
92-
MATCH Length sel_Term CaseDef*
91+
IF Length [INLINE] cond_Term then_Term else_Term
92+
MATCH Length (IMPLICIT | [INLINE] sel_Term) CaseDef*
9393
TRY Length expr_Term CaseDef* finalizer_Term?
9494
RETURN Length meth_ASTRef expr_Term?
9595
WHILE Length cond_Term body_Term

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,19 +414,31 @@ class TreePickler(pickler: TastyPickler) {
414414
writeByte(BLOCK)
415415
stats.foreach(preRegister)
416416
withLength { pickleTree(expr); stats.foreach(pickleTree) }
417-
case If(cond, thenp, elsep) =>
417+
case tree @ If(cond, thenp, elsep) =>
418418
writeByte(IF)
419-
withLength { pickleTree(cond); pickleTree(thenp); pickleTree(elsep) }
419+
withLength {
420+
if (tree.isInline) writeByte(INLINE)
421+
pickleTree(cond)
422+
pickleTree(thenp)
423+
pickleTree(elsep)
424+
}
420425
case Closure(env, meth, tpt) =>
421426
writeByte(LAMBDA)
422427
assert(env.isEmpty)
423428
withLength {
424429
pickleTree(meth)
425430
if (tpt.tpe.exists) pickleTpt(tpt)
426431
}
427-
case Match(selector, cases) =>
432+
case tree @ Match(selector, cases) =>
428433
writeByte(MATCH)
429-
withLength { pickleTree(selector); cases.foreach(pickleTree) }
434+
withLength {
435+
if (tree.isInline) {
436+
if (selector.isEmpty) writeByte(IMPLICIT)
437+
else { writeByte(INLINE); pickleTree(selector) }
438+
}
439+
else pickleTree(selector)
440+
tree.cases.foreach(pickleTree)
441+
}
430442
case CaseDef(pat, guard, rhs) =>
431443
writeByte(CASEDEF)
432444
withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) }

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,13 +1081,26 @@ class TreeUnpickler(reader: TastyReader,
10811081
val expansion = exprReader.readTerm() // need bindings in scope, so needs to be read before
10821082
Inlined(call, bindings, expansion)
10831083
case IF =>
1084-
If(readTerm(), readTerm(), readTerm())
1084+
if (nextByte == INLINE) {
1085+
readByte()
1086+
InlineIf(readTerm(), readTerm(), readTerm())
1087+
}
1088+
else
1089+
If(readTerm(), readTerm(), readTerm())
10851090
case LAMBDA =>
10861091
val meth = readTerm()
10871092
val tpt = ifBefore(end)(readTpt(), EmptyTree)
10881093
Closure(Nil, meth, tpt)
10891094
case MATCH =>
1090-
Match(readTerm(), readCases(end))
1095+
if (nextByte == IMPLICIT) {
1096+
readByte()
1097+
InlineMatch(EmptyTree, readCases(end))
1098+
}
1099+
else if (nextByte == INLINE) {
1100+
readByte()
1101+
InlineMatch(readTerm(), readCases(end))
1102+
}
1103+
else Match(readTerm(), readCases(end))
10911104
case RETURN =>
10921105
val from = readSymRef()
10931106
val expr = ifBefore(end)(readTerm(), EmptyTree)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import StdNames._
2020
import util.Positions._
2121
import Constants._
2222
import ScriptParsers._
23+
import Decorators._
2324
import scala.annotation.{tailrec, switch}
2425
import rewrites.Rewrites.patch
2526

@@ -1127,7 +1128,8 @@ object Parsers {
11271128
val start = in.offset
11281129
if (in.token == IMPLICIT || in.token == ERASED) {
11291130
val imods = modifiers(funArgMods)
1130-
implicitClosure(start, location, imods)
1131+
if (in.token == MATCH) implicitMatch(start, imods)
1132+
else implicitClosure(start, location, imods)
11311133
} else {
11321134
val saved = placeholderParams
11331135
placeholderParams = Nil
@@ -1207,7 +1209,21 @@ object Parsers {
12071209
case FOR =>
12081210
forExpr()
12091211
case _ =>
1210-
expr1Rest(postfixExpr(), location)
1212+
if (isIdent(nme.inline) && !in.inModifierPosition() && in.lookaheadIn(canStartExpressionTokens)) {
1213+
val start = in.skipToken()
1214+
in.token match {
1215+
case IF =>
1216+
ifExpr(start, InlineIf)
1217+
case _ =>
1218+
val t = postfixExpr()
1219+
if (in.token == MATCH) matchExpr(t, start, InlineMatch)
1220+
else {
1221+
syntaxErrorOrIncomplete(i"`match` or `if` expected but ${in.token} found")
1222+
t
1223+
}
1224+
}
1225+
}
1226+
else expr1Rest(postfixExpr(), location)
12111227
}
12121228

12131229
def expr1Rest(t: Tree, location: Location.Value): Tree = in.token match {
@@ -1221,7 +1237,7 @@ object Parsers {
12211237
case COLON =>
12221238
ascription(t, location)
12231239
case MATCH =>
1224-
matchExpr(t, startOffset(t))
1240+
matchExpr(t, startOffset(t), Match)
12251241
case _ =>
12261242
t
12271243
}
@@ -1266,13 +1282,36 @@ object Parsers {
12661282
}
12671283

12681284
/** `match' { CaseClauses }
1269-
* `match' { ImplicitCaseClauses }
12701285
*/
1271-
def matchExpr(t: Tree, start: Offset): Match =
1286+
def matchExpr(t: Tree, start: Offset, mkMatch: (Tree, List[CaseDef]) => Match) =
12721287
atPos(start, in.skipToken()) {
1273-
inBraces(Match(t, caseClauses(caseClause)))
1288+
inBraces(mkMatch(t, caseClauses(caseClause)))
12741289
}
12751290

1291+
/** `match' { ImplicitCaseClauses }
1292+
*/
1293+
def implicitMatch(start: Int, imods: Modifiers) = {
1294+
def markFirstIllegal(mods: List[Mod]) = mods match {
1295+
case mod :: _ => syntaxError(em"illegal modifier for implicit match", mod.pos)
1296+
case _ =>
1297+
}
1298+
imods.mods match {
1299+
case Mod.Implicit() :: mods => markFirstIllegal(mods)
1300+
case mods => markFirstIllegal(mods)
1301+
}
1302+
val result @ Match(t, cases) =
1303+
matchExpr(EmptyTree, start, InlineMatch)
1304+
for (CaseDef(pat, _, _) <- cases) {
1305+
def isImplicitPattern(pat: Tree) = pat match {
1306+
case Typed(pat1, _) => isVarPattern(pat1)
1307+
case pat => isVarPattern(pat)
1308+
}
1309+
if (!isImplicitPattern(pat))
1310+
syntaxError(em"not a legal pattern for an implicit match", pat.pos)
1311+
}
1312+
result
1313+
}
1314+
12761315
/** `match' { TypeCaseClauses }
12771316
*/
12781317
def matchType(bound: Tree, t: Tree): MatchTypeTree =
@@ -2620,6 +2659,8 @@ object Parsers {
26202659
var imods = modifiers(funArgMods)
26212660
if (isBindingIntro)
26222661
stats += implicitClosure(start, Location.InBlock, imods)
2662+
else if (in.token == MATCH)
2663+
stats += implicitMatch(start, imods)
26232664
else
26242665
stats +++= localDef(start, imods)
26252666
} else {

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,5 +249,5 @@ object Tokens extends TokensCommon {
249249

250250
final val numericLitTokens: TokenSet = BitSet(INTLIT, LONGLIT, FLOATLIT, DOUBLELIT)
251251

252-
final val softModifierNames = Set(nme.`inline`, nme.`opaque`)
252+
final val softModifierNames = Set(nme.inline, nme.opaque)
253253
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,15 +361,26 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
361361
case block: Block =>
362362
blockToText(block)
363363
case If(cond, thenp, elsep) =>
364+
val isInline = tree.isInstanceOf[Trees.InlineIf[_]]
364365
changePrec(GlobalPrec) {
365-
keywordStr("if ") ~ toText(cond) ~ (keywordText(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
366+
keywordStr(if (isInline) "inline if " else "if ") ~
367+
toText(cond) ~ (keywordText(" then") provided !cond.isInstanceOf[Parens]) ~~
368+
toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
366369
}
367370
case Closure(env, ref, target) =>
368371
"closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~
369372
toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")"
370373
case Match(sel, cases) =>
371-
if (sel.isEmpty) blockText(cases)
372-
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
374+
val isInline = tree.isInstanceOf[Trees.InlineMatch[_]]
375+
if (sel.isEmpty && !isInline) blockText(cases)
376+
else changePrec(GlobalPrec) {
377+
val selTxt: Text =
378+
if (isInline)
379+
if (sel.isEmpty) keywordStr("implicit")
380+
else keywordStr("inline ") ~ toText(sel)
381+
else toText(sel)
382+
selTxt ~ keywordStr(" match ") ~ blockText(cases)
383+
}
373384
case CaseDef(pat, guard, body) =>
374385
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
375386
case Labeled(bind, expr) =>

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ class PatternMatcher extends MiniPhase {
2828
override def phaseName: String = PatternMatcher.name
2929
override def runsAfter: Set[String] = Set(ElimRepeated.name)
3030

31-
override def transformMatch(tree: Match)(implicit ctx: Context): Tree = {
32-
val translated = new Translator(tree.tpe, this).translateMatch(tree)
31+
override def transformMatch(tree: Match)(implicit ctx: Context): Tree =
32+
if (tree.isInstanceOf[InlineMatch]) tree
33+
else {
34+
val translated = new Translator(tree.tpe, this).translateMatch(tree)
3335

34-
// check exhaustivity and unreachability
35-
val engine = new patmat.SpaceEngine
36-
engine.checkExhaustivity(tree)
37-
engine.checkRedundancy(tree)
36+
// check exhaustivity and unreachability
37+
val engine = new patmat.SpaceEngine
38+
engine.checkExhaustivity(tree)
39+
engine.checkRedundancy(tree)
3840

39-
translated.ensureConforms(tree.tpe)
40-
}
41+
translated.ensureConforms(tree.tpe)
42+
}
4143
}
4244

4345
object PatternMatcher {

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ object Applications {
6161
def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = {
6262
def tupleSelectors(n: Int, tp: Type): List[Type] = {
6363
val sel = extractorMemberType(tp, nme.selectorName(n), errorPos)
64-
// extractorMemberType will return NoType if this is the tail of tuple with an unknown tail
64+
// extractorMemberType will return NoType if this is the tail of tuple with an unknown tail
6565
// such as `Int *: T` where `T <: Tuple`.
6666
if (sel.exists) sel :: tupleSelectors(n + 1, tp) else Nil
6767
}
@@ -982,9 +982,6 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
982982
else trySelectUnapply(qual1)(_ => notAnExtractor(sel))
983983
}
984984

985-
if (unapplyFn.symbol.isInlineMethod)
986-
checkInInlineContext("implementation restriction: call to inline unapply", tree.pos)
987-
988985
/** Add a `Bind` node for each `bound` symbol in a type application `unapp` */
989986
def addBinders(unapp: Tree, bound: List[Symbol]) = unapp match {
990987
case TypeApply(fn, args) =>

0 commit comments

Comments
 (0)