Skip to content

Commit 3211985

Browse files
committed
Cleanup patmat based on feedback
from pr #174.
1 parent 2e6b343 commit 3211985

File tree

2 files changed

+54
-72
lines changed

2 files changed

+54
-72
lines changed

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

Lines changed: 53 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
4848

4949
private var _id = 0 // left for debuging
5050

51-
override def transformMatch(tree: tpd.Match)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
52-
val translated = new Translator()(ctx).translator.translateMatch(tree)
53-
translated.ensureConforms(tree.tpe)
51+
override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = {
52+
val translated = new Translator()(ctx).translator.translateMatch(tree)
53+
translated.ensureConforms(tree.tpe)
5454
}
5555

5656
class Translator(implicit ctx: Context) {
@@ -79,11 +79,18 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
7979
def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Symbol => Tree]): Tree
8080

8181
// local / context-free
82+
83+
/* cast b to tp */
8284
def _asInstanceOf(b: Symbol, tp: Type): Tree
85+
/* a check `checker` == binder */
8386
def _equals(checker: Tree, binder: Symbol): Tree
87+
/* b.isIsInstanceOf[tp] */
8488
def _isInstanceOf(b: Symbol, tp: Type): Tree
89+
/* tgt is expected to be a Seq, call tgt.drop(n) */
8590
def drop(tgt: Tree)(n: Int): Tree
91+
/* tgt is expected to have method apply(int), call tgt.drop(i) */
8692
def index(tgt: Tree)(i: Int): Tree
93+
/* make tree that accesses the i'th component of the tuple referenced by binder */
8794
def tupleSel(binder: Symbol)(i: Int): Tree
8895
}
8996

@@ -102,7 +109,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
102109
def codegen: AbsCodegen
103110

104111
abstract class CommonCodegen extends AbsCodegen {
105-
def tupleSel(binder: Symbol)(i: Int): Tree = ref(binder).select(nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder
112+
def tupleSel(binder: Symbol)(i: Int): Tree = ref(binder).select(nme.productAccessorName(i))
106113
def index(tgt: Tree)(i: Int): Tree = {
107114
if (i > 0) tgt.select(defn.Seq_apply).appliedTo(Literal(Constant(i)))
108115
else tgt.select(defn.Seq_head).appliedIfMethod
@@ -114,9 +121,9 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
114121
// for name-based matching, but this was an expedient route for the basics.
115122
def drop(tgt: Tree)(n: Int): Tree = {
116123
def callDirect = tgt.select(nme.drop).appliedTo(Literal(Constant(n)))
117-
def callRuntime = ref(ctx.definitions.traversableDropMethod).appliedTo(tgt, Literal(Constant(n)))
124+
def callRuntime = ref(defn.traversableDropMethod).appliedTo(tgt, Literal(Constant(n)))
118125

119-
def needsRuntime = (tgt.tpe ne null) && tgt.tpe.baseTypeRef(ctx.definitions.SeqType.classSymbol).member(nme.drop).exists /*typeOfMemberNamedDrop(tgt.tpe) == NoType*/
126+
def needsRuntime = tgt.tpe derivesFrom defn.SeqClass /*typeOfMemberNamedDrop(tgt.tpe) == NoType*/
120127

121128
if (needsRuntime) callRuntime else callDirect
122129
}
@@ -151,20 +158,18 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
151158
var replaced = 0
152159
val toAdapted = (from zip to) map {
153160
case (orig, nw) =>
154-
nw.ensureConforms(AndType(orig.info, nw.tpe))
161+
if (nw.tpe <:< orig.info) nw else nw.ensureConforms(orig.info & nw.tpe)
155162
}
156163

157-
val identReplace: tpd.Tree => tpd.Tree = _ match {
158-
case t:Ident =>
164+
val identReplace: Ident => Tree = { ident =>
159165
def subst(from: List[Symbol], to: List[Tree]): Tree =
160-
if (from.isEmpty) t
161-
else if (t.symbol == from.head) {
166+
if (from.isEmpty) ident
167+
else if (ident.symbol == from.head) {
162168
replaced += 1
163169
to.head //typedIfOrigTyped(to.head.shallowDuplicate.setPos(tree.pos), tree.tpe)
164170
}
165171
else subst(from.tail, to.tail)
166172
subst(from, toAdapted)
167-
case t => t
168173
}
169174
val start = System.currentTimeMillis()
170175
val res = new tpd.TreeMap() {
@@ -226,7 +231,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
226231
// must compute catchAll after caseLabels (side-effects nextCase)
227232
// catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default)
228233
// if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd
229-
val catchAllDef = matchFailGen.map { matchFailGen => matchFailGen(scrutSym)}
234+
val catchAllDef = matchFailGen.map { _(scrutSym)}
230235
.getOrElse(Throw(New(defn.MatchErrorType, List(ref(scrutSym)))))
231236

232237
val matchFail = newSynthCaseLabel(ctx.freshName("matchFail"), MethodType(Nil, restpe))
@@ -278,7 +283,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
278283
List(ValDef(prevSym, prev)),
279284
// must be isEmpty and get as we don't control the target of the call (prev is an extractor call)
280285
ifThenElseZero(
281-
ref(prevSym).select(nme.isDefined).select(ctx.definitions.Boolean_!),
286+
ref(prevSym).select(nme.isDefined).select(defn.Boolean_!),
282287
Substitution(b, prevValue)(next)
283288
)
284289
)
@@ -333,9 +338,11 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
333338
def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] =
334339
None // todo
335340

336-
abstract class TreeMaker{
341+
abstract class TreeMaker {
337342
def pos: Position
338343

344+
private[this] var currSub: Substitution = null
345+
339346
/** captures the scope and the value of the bindings in patterns
340347
* important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed)
341348
*/
@@ -352,7 +359,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
352359
}
353360
else currSub = outerSubst >> substitution
354361
}
355-
private[this] var currSub: Substitution = null
356362

357363
/** The substitution that specifies the trees that compute the values of the subpattern binders.
358364
*
@@ -419,10 +425,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
419425
// unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns
420426
protected val debugInfoEmitVars = true //!settings.optimise.value
421427

428+
/**
429+
* Tree maker that captures sub pattern values during pattern match.
430+
*/
422431
sealed trait PreserveSubPatBinders extends TreeMaker {
423-
val subPatBinders: List[Symbol]
424-
val subPatRefs: List[Tree]
425-
val ignoredSubPatBinders: Set[Symbol]
432+
val subPatBinders: List[Symbol] // captured values
433+
val subPatRefs: List[Tree] // trees that will replace references to subPatBinders
434+
val ignoredSubPatBinders: Set[Symbol] // ignored as they aren't used in body of pattern
426435

427436
// unless `debugInfoEmitVars`, this set should contain the bare minimum for correctness
428437
// mutable case class fields need to be stored regardless (SI-5158, SI-6070) -- see override in ProductExtractorTreeMaker
@@ -567,10 +576,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
567576
def extraStoredBinders: Set[Symbol] = mutableBinders.toSet
568577

569578
def chainBefore(next: Tree)(casegen: Casegen): Tree = {
570-
val nullCheck: Tree = ref(prevBinder).select(ctx.definitions.Object_ne).appliedTo(Literal(Constant(null)))
579+
val nullCheck: Tree = ref(prevBinder).select(defn.Object_ne).appliedTo(Literal(Constant(null)))
571580
val cond: Option[Tree] =
572581
if (binderKnownNonNull) extraCond
573-
else extraCond.map(nullCheck.select(ctx.definitions.Boolean_&&).appliedTo).orElse(Some(nullCheck))
582+
else extraCond.map(nullCheck.select(defn.Boolean_&&).appliedTo).orElse(Some(nullCheck))
574583

575584
cond match {
576585
case Some(cond: Tree) =>
@@ -621,12 +630,12 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
621630
object treeCondStrategy extends TypeTestCondStrategy {
622631
type Result = Tree
623632

624-
def and(a: Result, b: Result): Result = a.select(ctx.definitions.Boolean_&&).appliedTo(b)
633+
def and(a: Result, b: Result): Result = a.select(defn.Boolean_&&).appliedTo(b)
625634
def tru = Literal(Constant(true))
626635
def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp)
627-
def nonNullTest(testedBinder: Symbol) = ref(testedBinder).select(ctx.definitions.Object_ne).appliedTo(Literal(Constant(null)))
636+
def nonNullTest(testedBinder: Symbol) = ref(testedBinder).select(defn.Object_ne).appliedTo(Literal(Constant(null)))
628637
def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder)
629-
def eqTest(pat: Tree, testedBinder: Symbol) = ref(testedBinder).select(ctx.definitions.Object_eq).appliedTo(pat)
638+
def eqTest(pat: Tree, testedBinder: Symbol) = ref(testedBinder).select(defn.Object_eq).appliedTo(pat)
630639

631640
def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = {
632641
val expectedOuter = expectedTp.normalizedPrefix match {
@@ -641,7 +650,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
641650

642651
val expectedClass = expectedTp.dealias.classSymbol.asClass
643652
ExplicitOuter.ensureOuterAccessors(expectedClass)
644-
codegen._asInstanceOf(testedBinder, expectedTp).select(ExplicitOuter.outerAccessor(expectedClass)).select(ctx.definitions.Object_eq).appliedTo(expectedOuter)
653+
codegen._asInstanceOf(testedBinder, expectedTp).select(ExplicitOuter.outerAccessor(expectedClass)).select(defn.Object_eq).appliedTo(expectedOuter)
645654
}
646655
}
647656

@@ -738,10 +747,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
738747

739748
def testedWide = testedBinder.info.widen
740749
def expectedWide = expectedTp.widen
741-
def isAnyRef = testedWide <:< ctx.definitions.AnyRefType
750+
def isAnyRef = testedWide <:< defn.AnyRefType
742751
def isAsExpected = testedWide <:< expectedTp
743-
def isExpectedPrimitiveType = isAsExpected && ctx.definitions.ScalaValueClasses.contains(expectedTp.classSymbol)
744-
def isExpectedReferenceType = isAsExpected && (expectedTp <:< ctx.definitions.AnyRefType)
752+
def isExpectedPrimitiveType = isAsExpected && defn.ScalaValueClasses.contains(expectedTp.classSymbol)
753+
def isExpectedReferenceType = isAsExpected && (expectedTp <:< defn.AnyRefType)
745754
def mkNullTest = nonNullTest(testedBinder)
746755
def mkOuterTest = outerTest(testedBinder, expectedTp)
747756
def mkTypeTest = typeTest(testedBinder, expectedWide)
@@ -823,7 +832,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
823832
((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(Literal(Constant(true)))))(casegen))
824833
)
825834

826-
val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, ctx.definitions.BooleanType)(combinedAlts, Some((x: Symbol) => Literal(Constant(false))))
835+
val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, defn.BooleanType)(combinedAlts, Some((x: Symbol) => Literal(Constant(false))))
827836
codegenAlt.ifThenElseZero(findAltMatcher, substitution(next))
828837
}
829838
}
@@ -935,8 +944,9 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
935944
def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent]
936945

937946
def isVarPattern(pat: Tree): Boolean = pat match {
938-
case x: Ident => !isBackquoted(x) && nme.isVariableName(x.name)
939-
case _ => false
947+
case x: BackquotedIdent => false
948+
case x: Ident => nme.isVariableName(x.name)
949+
case _ => false
940950
}
941951

942952
/** A conservative approximation of which patterns do not discern anything.
@@ -970,27 +980,17 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
970980
}
971981
}
972982

973-
// Always map repeated params to sequences
974-
private def setVarInfo(sym: Symbol, info: Type) ={
975-
//setInfo debug.patmatResult(s"changing ${sym.defString} to")(repeatedToSeq(info))
976-
// if(info!= NoType && !(sym.info =:= info))
977-
// assert(false, "bug in porting pattern matcher")
978-
ctx.debuglog(s"scalac would be resetting info of $sym from ${sym.info} to $info")
979-
sym
980-
}
981-
982-
983983
def newBoundTree(tree: Tree, pt: Type): BoundTree = tree match {
984984
case SymbolBound(sym, Typed(subpat, tpe)) => BoundTree(freshSym(tree.pos, pt, prefix = "pi"), tree)
985-
case SymbolBound(sym, expr) => BoundTree(setVarInfo(sym, pt), expr)
985+
case SymbolBound(sym, expr) => BoundTree(sym, expr)
986986
case _ => BoundTree(freshSym(tree.pos, pt, prefix = "p"), tree)
987987
}
988988

989989
final case class BoundTree(binder: Symbol, tree: Tree) {
990990
private lazy val extractor = ExtractorCall(tree, binder)
991991

992992
def pos = tree.pos
993-
def tpe = binder.info.dealias.widen // the type of the variable bound to the pattern
993+
def tpe = binder.info.widenDealias
994994
def pt = unbound match {
995995
// case Star(tpt) => this glbWith seqType(tpt.tpe) dd todo:
996996
case TypeBound(tpe) => tpe
@@ -1009,7 +1009,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
10091009

10101010
object TypeBound {
10111011
def unapply(tree: Tree): Option[Type] = tree match {
1012-
case Typed(_, _) if tree.tpe != null => Some(tree.tpe)
1012+
case Typed(_, _) => Some(tree.typeOpt)
10131013
case _ => None
10141014
}
10151015
}
@@ -1046,7 +1046,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
10461046
// TODO: paramType may contain unbound type params (run/t2800, run/t3530)
10471047
val makers = (
10481048
// Statically conforms to paramType
1049-
if (this ensureConformsTo paramType) treeMaker(binder, false, pos, tpe) :: Nil
1049+
if (tpe <:< paramType) treeMaker(binder, false, pos, tpe) :: Nil
10501050
else typeTest :: extraction :: Nil
10511051
)
10521052
step(makers: _*)(extractor.subBoundTrees: _*)
@@ -1078,20 +1078,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
10781078
}
10791079
def translate(): List[TreeMaker] = nextStep() merge (_.translate())
10801080

1081-
private def setInfo(paramType: Type): Boolean = {
1082-
setVarInfo(binder, paramType)
1083-
true
1084-
}
1085-
// If <:< but not =:=, no type test needed, but the tree maker relies on the binder having
1086-
// exactly paramType (and not just some type compatible with it.) SI-6624 shows this is necessary
1087-
// because apparently patBinder may have an unfortunate type (.decls don't have the case field
1088-
// accessors) TODO: get to the bottom of this -- I assume it happens when type checking
1089-
// infers a weird type for an unapply call. By going back to the parameterType for the
1090-
// extractor call we get a saner type, so let's just do that for now.
1091-
def ensureConformsTo(paramType: Type): Boolean = (
1092-
(tpe =:= paramType)
1093-
|| (tpe <:< paramType) && setInfo(paramType)
1094-
)
10951081

10961082
private def concreteType = tpe.bounds.hi
10971083
private def unbound = unbind(tree)
@@ -1131,16 +1117,11 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
11311117
isDefaultCase(cdef)
11321118
}
11331119

1134-
def isNonBottomSubClass(thiz: Symbol, that: Symbol)(implicit ctx: Context): Boolean = (
1135-
(thiz eq that) || thiz.isError || that.isError ||
1136-
thiz.info.baseClasses.contains(that)
1137-
)
1138-
11391120
private def isSimpleThrowable(tp: Type)(implicit ctx: Context): Boolean = tp match {
11401121
case tp @ TypeRef(pre, _) =>
11411122
val sym = tp.symbol
11421123
(pre == NoPrefix || pre.widen.typeSymbol.isStatic) &&
1143-
(isNonBottomSubClass(sym, ctx.definitions.ThrowableClass)) && /* bq */ !(sym is Flags.Trait)
1124+
(sym.derivesFrom(defn.ThrowableClass)) && /* bq */ !(sym is Flags.Trait)
11441125
case _ =>
11451126
false
11461127
}
@@ -1209,7 +1190,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12091190
val bindersAndCases = caseDefs map { caseDef =>
12101191
// generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there)
12111192
// if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this)
1212-
val caseScrutSym = freshSym(pos, pureType(ctx.definitions.ThrowableType))
1193+
val caseScrutSym = freshSym(pos, pureType(defn.ThrowableType))
12131194
(caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, pt)(caseDef), EmptySubstitution))
12141195
}
12151196
@@ -1219,10 +1200,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12191200
}
12201201
12211202
val catches = if (swatches.nonEmpty) swatches else {
1222-
val scrutSym = freshSym(pos, pureType(ctx.definitions.ThrowableType))
1203+
val scrutSym = freshSym(pos, pureType(defn.ThrowableType))
12231204
val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, pt)(caseDef), EmptySubstitution))}
12241205
1225-
val exSym = freshSym(pos, pureType(ctx.definitions.ThrowableType), "ex")
1206+
val exSym = freshSym(pos, pureType(defn.ThrowableType), "ex")
12261207
12271208
List(
12281209
CaseDef(
@@ -1233,7 +1214,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12331214
)
12341215
}
12351216
1236-
/*typer.typedCases(*/catches/*, ctx.definitions.ThrowableType, WildcardType)*/
1217+
/*typer.typedCases(*/catches/*, defn.ThrowableType, WildcardType)*/
12371218
}*/
12381219

12391220
/** The translation of `pat if guard => body` has two aspects:
@@ -1811,15 +1792,15 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
18111792

18121793
//println(s"${_id}unapplyArgs(${result.widen}")
18131794
val expanded:List[Type] = /*(
1814-
if (result =:= ctx.definitions.BooleanType) Nil
1795+
if (result =:= defn.BooleanType) Nil
18151796
else if(defn.isProductSubType(result)) productSelectorTypes(result)
18161797
else if(result.classSymbol is Flags.CaseClass) result.decls.filter(x => x.is(Flags.CaseAccessor) && x.is(Flags.Method)).map(_.info).toList
18171798
else result.select(nme.get) :: Nil
18181799
)*/
18191800
if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && resultOfGet.exists)
18201801
getUnapplySelectors(resultOfGet, args)
18211802
else if (defn.isProductSubType(resultType)) productSelectorTypes(resultType)
1822-
else if (resultType =:= ctx.definitions.BooleanType) Nil
1803+
else if (resultType =:= defn.BooleanType) Nil
18231804
else {
18241805
ctx.error(i"invalid return type in Unapply node: $resultType")
18251806
Nil
@@ -1836,7 +1817,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
18361817
/** Converts a T => (A, B, C) extractor to a T => ((A, B, CC)) extractor.
18371818
*/
18381819
def tupleExtractor(extractor: Extractor): Extractor =
1839-
extractor.copy(fixed = ctx.definitions.tupleType(extractor.fixed) :: Nil)
1820+
extractor.copy(fixed = defn.tupleType(extractor.fixed) :: Nil)
18401821

18411822
private def validateAligned(tree: Tree, aligned: Aligned): Aligned = {
18421823
import aligned._

tests/pos/Patterns.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ object Patterns {
4040
case List(x, z) => x
4141
case List(x) => x
4242
case List() => ""
43+
case x @ _ => "wildcard"
4344
}
4445
val yy: String = y
4546
}

0 commit comments

Comments
 (0)