@@ -10,6 +10,7 @@ import util.Spans._
1010import typer .Applications .*
1111import SymUtils ._
1212import TypeUtils .*
13+ import Annotations .*
1314import Flags ._ , Constants ._
1415import Decorators ._
1516import NameKinds .{PatMatStdBinderName , PatMatAltsName , PatMatResultName }
@@ -708,9 +709,9 @@ object PatternMatcher {
708709 // ----- Generating trees from plans ---------------
709710
710711 /** The condition a test plan rewrites to */
711- private def emitCondition (plan : TestPlan ): Tree = {
712+ private def emitCondition (plan : TestPlan ): Tree =
712713 val scrutinee = plan.scrutinee
713- (plan.test: @ unchecked) match {
714+ (plan.test: @ unchecked) match
714715 case NonEmptyTest =>
715716 constToLiteral(
716717 scrutinee
@@ -738,41 +739,49 @@ object PatternMatcher {
738739 case TypeTest (tpt, trusted) =>
739740 val expectedTp = tpt.tpe
740741
741- // An outer test is needed in a situation like `case x: y.Inner => ...`
742- def outerTestNeeded : Boolean = {
743- def go (expected : Type ): Boolean = expected match {
744- case tref @ TypeRef (pre : SingletonType , _) =>
745- tref.symbol.isClass &&
746- ExplicitOuter .needsOuterIfReferenced(tref.symbol.asClass)
747- case AppliedType (tpe, _) => go(tpe)
748- case _ =>
749- false
750- }
751- // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest`
752- // generates an outer test based on `patType.prefix` with automatically dealises.
753- go(expectedTp.dealias)
754- }
742+ def typeTest (scrut : Tree , expected : Type ): Tree =
743+ val ttest = scrut.select(defn.Any_typeTest ).appliedToType(expected)
744+ if trusted then ttest.pushAttachment(TrustedTypeTestKey , ())
745+ ttest
755746
756- def outerTest : Tree = thisPhase.transformFollowingDeep {
757- val expectedOuter = singleton(expectedTp.normalizedPrefix)
758- val expectedClass = expectedTp.dealias.classSymbol.asClass
759- ExplicitOuter .ensureOuterAccessors(expectedClass)
760- scrutinee.ensureConforms(expectedTp)
761- .outerSelect(1 , expectedClass.owner.typeRef)
762- .select(defn.Object_eq )
763- .appliedTo(expectedOuter)
764- }
747+ /** An outer test is needed in a situation like `case x: y.Inner => ...
748+ * or like case x: O#Inner if the owner of Inner is not a subclass of O.
749+ * Outer tests are added here instead of in TypeTestsCasts since they
750+ * might cause outer accessors to be added to inner classes (via ensureOuterAccessors)
751+ * and therefore have to run before ExplicitOuter.
752+ */
753+ def addOuterTest (tree : Tree , expected : Type ): Tree = expected.dealias match
754+ case tref @ TypeRef (pre, _) =>
755+ tref.symbol match
756+ case expectedCls : ClassSymbol if ExplicitOuter .needsOuterIfReferenced(expectedCls) =>
757+ def selectOuter =
758+ ExplicitOuter .ensureOuterAccessors(expectedCls)
759+ scrutinee.ensureConforms(expected).outerSelect(1 , expectedCls.owner.typeRef)
760+ if pre.isSingleton then
761+ val expectedOuter = singleton(pre)
762+ tree.and(selectOuter.select(defn.Object_eq ).appliedTo(expectedOuter))
763+ else if ! expectedCls.isStatic
764+ && expectedCls.owner.isType
765+ && ! expectedCls.owner.derivesFrom(pre.classSymbol)
766+ then
767+ val testPre =
768+ if expected.hasAnnotation(defn.UncheckedAnnot ) then
769+ AnnotatedType (pre, Annotation (defn.UncheckedAnnot , tree.span))
770+ else pre
771+ tree.and(typeTest(selectOuter, testPre))
772+ else tree
773+ case _ => tree
774+ case AppliedType (tycon, _) =>
775+ addOuterTest(tree, tycon)
776+ case _ =>
777+ tree
765778
766- expectedTp.dealias match {
779+ expectedTp.dealias match
767780 case expectedTp : SingletonType =>
768781 scrutinee.isInstance(expectedTp) // will be translated to an equality test
769782 case _ =>
770- val typeTest = scrutinee.select(defn.Any_typeTest ).appliedToType(expectedTp)
771- if (trusted) typeTest.pushAttachment(TrustedTypeTestKey , ())
772- if (outerTestNeeded) typeTest.and(outerTest) else typeTest
773- }
774- }
775- }
783+ addOuterTest(typeTest(scrutinee, expectedTp), expectedTp)
784+ end emitCondition
776785
777786 @ tailrec
778787 private def canFallThrough (plan : Plan ): Boolean = plan match {
0 commit comments