Skip to content
This repository
Browse code

SI-6815 untangle isStable and hasVolatileType

`Symbol::isStable` is now independent of `Symbol::hasVolatileType`,
so that we can allow stable identifiers that are volatile in ident patterns.

This split is validated by SI-6815 and the old logic in RefChecks,
which seems to assume this independence, and thus I don't think ever worked:

```
if (member.isStable && !otherTp.isVolatile) {
  if (memberTp.isVolatile)
    overrideError("has a volatile type; cannot override a member with non-volatile type")
```

Introduces `admitsTypeSelection` and `isStableIdentifierPattern` in treeInfo,
and uses them instead of duplicating that logic all over the place.

Since volatility only matters in the context of type application,
`isStableIdentifierPattern` is used to check patterns (resulting in `==` checks)
and imports.
  • Loading branch information...
commit fada1ef6b315326ac0329d9e78951cfc95ad0eb0 1 parent 97d5179
Adriaan Moors authored
2  src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -141,7 +141,7 @@ trait ContextErrors {
141 141
         }
142 142
         issueNormalTypeError(tree,
143 143
           "stable identifier required, but "+tree+" found." + (
144  
-          if (isStableExceptVolatile(tree)) addendum else ""))
  144
+          if (treeInfo.hasVolatileType(tree)) addendum else ""))
145 145
         setError(tree)
146 146
       }
147 147
 
5  src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1342,13 +1342,16 @@ trait Namers extends MethodSynthesis {
1342 1342
     private def importSig(imp: Import) = {
1343 1343
       val Import(expr, selectors) = imp
1344 1344
       val expr1 = typer.typedQualifier(expr)
1345  
-      typer checkStable expr1
  1345
+
1346 1346
       if (expr1.symbol != null && expr1.symbol.isRootPackage)
1347 1347
         RootImportError(imp)
1348 1348
 
1349 1349
       if (expr1.isErrorTyped)
1350 1350
         ErrorType
1351 1351
       else {
  1352
+        if (!treeInfo.isStableIdentifierPattern(expr1))
  1353
+          typer.TyperErrorGen.UnstableTreeError(expr1)
  1354
+
1352 1355
         val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import]
1353 1356
         checkSelectors(newImport)
1354 1357
         transformed(imp) = newImport
2  src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -202,7 +202,7 @@ trait NamesDefaults { self: Analyzer =>
202 202
           if (module == NoSymbol) None
203 203
           else {
204 204
             val ref = atPos(pos.focus)(gen.mkAttributedRef(pre, module))
205  
-            if (module.isStable && pre.isStable)    // fixes #4524. the type checker does the same for
  205
+            if (treeInfo.admitsTypeSelection(ref))  // fixes #4524. the type checker does the same for
206 206
               ref.setType(singleType(pre, module))  // typedSelect, it calls "stabilize" on the result.
207 207
             Some(ref)
208 208
           }
5  src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -511,7 +511,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
511 511
             }
512 512
 
513 513
             if (member.isStable && !otherTp.isVolatile) {
514  
-	            if (memberTp.isVolatile)
  514
+              // (1.4), pt 2 -- member.isStable && memberTp.isVolatile started being possible after SI-6815
  515
+              // (before SI-6815, !symbol.tpe.isVolatile was implied by symbol.isStable)
  516
+              // TODO: allow overriding when @uncheckedStable?
  517
+              if (memberTp.isVolatile)
515 518
                 overrideError("has a volatile type; cannot override a member with non-volatile type")
516 519
               else memberTp.dealiasWiden.resultType match {
517 520
                 case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) =>
47  src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -587,7 +587,7 @@ trait Typers extends Adaptations with Tags {
587 587
       }
588 588
 
589 589
     /** Post-process an identifier or selection node, performing the following:
590  
-     *  1. Check that non-function pattern expressions are stable
  590
+     *  1. Check that non-function pattern expressions are stable (ignoring volatility concerns -- SI-6815)
591 591
      *  2. Check that packages and static modules are not used as values
592 592
      *  3. Turn tree type into stable type if possible and required by context.
593 593
      *  4. Give getClass calls a more precise type based on the type of the target of the call.
@@ -602,16 +602,18 @@ trait Typers extends Adaptations with Tags {
602 602
       if (tree.isErrorTyped) tree
603 603
       else if (mode.inPatternNotFunMode && tree.isTerm) { // (1)
604 604
         if (sym.isValue) {
605  
-          val tree1 = checkStable(tree)
606  
-          // A module reference in a pattern has type Foo.type, not "object Foo"
607  
-          if (sym.isModuleNotMethod) tree1 setType singleType(pre, sym)
608  
-          else tree1
  605
+          if (tree.isErrorTyped) tree
  606
+          else if (treeInfo.isStableIdentifierPattern(tree)) {
  607
+            // A module reference in a pattern has type Foo.type, not "object Foo"
  608
+            if (sym.isModuleNotMethod) tree setType singleType(pre, sym)
  609
+            else tree
  610
+          } else UnstableTreeError(tree)
609 611
         }
610 612
         else fail()
611 613
       } else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2)
612 614
         fail()
613 615
       } else {
614  
-        if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) &&
  616
+        if (treeInfo.admitsTypeSelection(tree) &&
615 617
             (isStableContext(tree, mode, pt) || sym.isModuleNotMethod))
616 618
           tree.setType(singleType(pre, sym))
617 619
         // To fully benefit from special casing the return type of
@@ -4442,6 +4444,7 @@ trait Typers extends Adaptations with Tags {
4442 4444
         }
4443 4445
 
4444 4446
       def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = {
  4447
+        // TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)`
4445 4448
         val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable
4446 4449
         val funpt = if (isPatternMode) pt else WildcardType
4447 4450
         val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null
@@ -4744,16 +4747,20 @@ trait Typers extends Adaptations with Tags {
4744 4747
             typedSelect(tree, qual1, nme.CONSTRUCTOR)
4745 4748
           case _ =>
4746 4749
             if (Statistics.canEnable) Statistics.incCounter(typedSelectCount)
4747  
-            var qual1 = checkDead(typedQualifier(qual, mode))
4748  
-            if (name.isTypeName) qual1 = checkStable(qual1)
  4750
+            val qualTyped = checkDead(typedQualifier(qual, mode))
  4751
+            val qualStableOrError =
  4752
+              if (qualTyped.isErrorTyped || !name.isTypeName || treeInfo.admitsTypeSelection(qualTyped))
  4753
+                qualTyped
  4754
+              else
  4755
+                UnstableTreeError(qualTyped)
4749 4756
 
4750 4757
             val tree1 = // temporarily use `filter` and an alternative for `withFilter`
4751 4758
               if (name == nme.withFilter)
4752  
-                silent(_ => typedSelect(tree, qual1, name)) orElse { _ =>
4753  
-                    silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match {
  4759
+                silent(_ => typedSelect(tree, qualStableOrError, name)) orElse { _ =>
  4760
+                    silent(_ => typed1(Select(qualStableOrError, nme.filter) setPos tree.pos, mode, pt)) match {
4754 4761
                       case SilentResultValue(result2) =>
4755 4762
                         unit.deprecationWarning(
4756  
-                          tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen +
  4763
+                          tree.pos, "`withFilter' method does not yet exist on " + qualStableOrError.tpe.widen +
4757 4764
                             ", using `filter' method instead")
4758 4765
                         result2
4759 4766
                       case SilentTypeError(err) =>
@@ -4761,14 +4768,14 @@ trait Typers extends Adaptations with Tags {
4761 4768
                     }
4762 4769
                 }
4763 4770
               else
4764  
-                typedSelect(tree, qual1, name)
  4771
+                typedSelect(tree, qualStableOrError, name)
4765 4772
 
4766 4773
             if (tree.isInstanceOf[PostfixSelect])
4767 4774
               checkFeature(tree.pos, PostfixOpsFeature, name.decode)
4768 4775
             if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember)
4769 4776
               checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString)
4770 4777
 
4771  
-            if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name)
  4778
+            if (qualStableOrError.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name)
4772 4779
             else tree1
4773 4780
         }
4774 4781
       }
@@ -5161,12 +5168,16 @@ trait Typers extends Adaptations with Tags {
5161 5168
       }
5162 5169
 
5163 5170
       def typedSingletonTypeTree(tree: SingletonTypeTree) = {
5164  
-        val ref1 = checkStable(
5165  
-          context.withImplicitsDisabled(
  5171
+        val refTyped =
  5172
+          context.withImplicitsDisabled {
5166 5173
             typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe)
5167  
-          )
5168  
-        )
5169  
-        tree setType ref1.tpe.resultType
  5174
+          }
  5175
+
  5176
+        if (!refTyped.isErrorTyped)
  5177
+          tree setType refTyped.tpe.resultType
  5178
+
  5179
+        if (treeInfo.admitsTypeSelection(refTyped)) tree
  5180
+        else UnstableTreeError(refTyped)
5170 5181
       }
5171 5182
 
5172 5183
       def typedSelectFromTypeTree(tree: SelectFromTypeTree) = {
6  src/interactive/scala/tools/nsc/interactive/Global.scala
@@ -925,14 +925,14 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
925 925
   }
926 926
 
927 927
   def stabilizedType(tree: Tree): Type = tree match {
928  
-    case Ident(_) if tree.symbol.isStable =>
  928
+    case Ident(_) if treeInfo.admitsTypeSelection(tree) =>
929 929
       singleType(NoPrefix, tree.symbol)
930  
-    case Select(qual, _) if qual.tpe != null && tree.symbol.isStable =>
  930
+    case Select(qual, _) if treeInfo.admitsTypeSelection(tree) =>
931 931
       singleType(qual.tpe, tree.symbol)
932 932
     case Import(expr, selectors) =>
933 933
       tree.symbol.info match {
934 934
         case analyzer.ImportType(expr) => expr match {
935  
-          case s@Select(qual, name) => singleType(qual.tpe, s.symbol)
  935
+          case s@Select(qual, name) if treeInfo.admitsTypeSelection(expr) => singleType(qual.tpe, s.symbol)
936 936
           case i : Ident => i.tpe
937 937
           case _ => tree.tpe
938 938
         }
2  src/interactive/scala/tools/nsc/interactive/Picklers.scala
@@ -96,7 +96,7 @@ trait Picklers { self: Global =>
96 96
       if (!sym.isRoot) {
97 97
         ownerNames(sym.owner, buf)
98 98
         buf += (if (sym.isModuleClass) sym.sourceModule else sym).name
99  
-        if (!sym.isType && !sym.isStable) {
  99
+        if (!sym.isType && !sym.isStable) { // TODO: what's the reasoning behind this condition!?
100 100
           val sym1 = sym.owner.info.decl(sym.name)
101 101
           if (sym1.isOverloaded) {
102 102
             val index = sym1.alternatives.indexOf(sym)
16  src/reflect/scala/reflect/internal/Symbols.scala
@@ -790,8 +790,12 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
790 790
     /** Is this symbol an accessor method for outer? */
791 791
     final def isOuterField = isArtifact && (unexpandedName == nme.OUTER_LOCAL)
792 792
 
793  
-    /** Does this symbol denote a stable value? */
794  
-    def isStable = false
  793
+    /** Does this symbol denote a stable value, ignoring volatility?
  794
+     *
  795
+     * Stability and volatility are checked separately to allow volatile paths in patterns that amount to equality checks. SI-6815
  796
+     */
  797
+    final def isStable        = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag)
  798
+    final def hasVolatileType = tpe.isVolatile && !hasAnnotation(uncheckedStableClass)
795 799
 
796 800
     /** Does this symbol denote the primary constructor of its enclosing class? */
797 801
     final def isPrimaryConstructor =
@@ -2589,13 +2593,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
2589 2593
     override def isMixinConstructor = name == nme.MIXIN_CONSTRUCTOR
2590 2594
     override def isConstructor      = nme.isConstructorName(name)
2591 2595
 
2592  
-    override def isPackageObject  = isModule && (name == nme.PACKAGE)
2593  
-    override def isStable = !isUnstable
2594  
-    private def isUnstable = (
2595  
-         isMutable
2596  
-      || (hasFlag(METHOD | BYNAMEPARAM) && !hasFlag(STABLE))
2597  
-      || (tpe.isVolatile && !hasAnnotation(uncheckedStableClass))
2598  
-    )
  2596
+    override def isPackageObject = isModule && (name == nme.PACKAGE)
2599 2597
 
2600 2598
     // The name in comments is what it is being disambiguated from.
2601 2599
     // TODO - rescue CAPTURED from BYNAMEPARAM so we can see all the names.
20  src/reflect/scala/reflect/internal/TreeGen.scala
@@ -142,17 +142,15 @@ abstract class TreeGen extends macros.TreeBuilder {
142 142
   }
143 143
 
144 144
   /** Computes stable type for a tree if possible */
145  
-  def stableTypeFor(tree: Tree): Option[Type] = tree match {
146  
-    case This(_) if tree.symbol != null && !tree.symbol.isError =>
147  
-      Some(ThisType(tree.symbol))
148  
-    case Ident(_) if tree.symbol.isStable =>
149  
-      Some(singleType(tree.symbol.owner.thisType, tree.symbol))
150  
-    case Select(qual, _) if ((tree.symbol ne null) && (qual.tpe ne null)) && // turned assert into guard for #4064
151  
-                            tree.symbol.isStable && qual.tpe.isStable =>
152  
-      Some(singleType(qual.tpe, tree.symbol))
153  
-    case _ =>
154  
-      None
155  
-  }
  145
+  def stableTypeFor(tree: Tree): Option[Type] =
  146
+    if (treeInfo.admitsTypeSelection(tree))
  147
+      tree match {
  148
+        case This(_)         => Some(ThisType(tree.symbol))
  149
+        case Ident(_)        => Some(singleType(tree.symbol.owner.thisType, tree.symbol))
  150
+        case Select(qual, _) => Some(singleType(qual.tpe, tree.symbol))
  151
+        case _               => None
  152
+      }
  153
+    else None
156 154
 
157 155
   /** Builds a reference with stable type to given symbol */
158 156
   def mkAttributedStableRef(pre: Type, sym: Symbol): Tree =
82  src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -18,7 +18,7 @@ abstract class TreeInfo {
18 18
   val global: SymbolTable
19 19
 
20 20
   import global._
21  
-  import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType }
  21
+  import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType, uncheckedStableClass }
22 22
 
23 23
   /* Does not seem to be used. Not sure what it does anyway.
24 24
   def isOwnerDefinition(tree: Tree): Boolean = tree match {
@@ -66,6 +66,80 @@ abstract class TreeInfo {
66 66
       false
67 67
   }
68 68
 
  69
+  /** Is `tree` a path, defined as follows? (Spec: 3.1 Paths)
  70
+   *
  71
+   * - The empty path ε (which cannot be written explicitly in user programs).
  72
+   * - C.this, where C references a class.
  73
+   * - p.x where p is a path and x is a stable member of p.
  74
+   * - C.super.x or C.super[M].x where C references a class
  75
+   *   and x references a stable member of the super class or designated parent class M of C.
  76
+   *
  77
+   * NOTE: Trees with errors are (mostly) excluded.
  78
+   *
  79
+   * Path ::= StableId | [id ‘.’] this
  80
+   *
  81
+   */
  82
+  def isPath(tree: Tree, allowVolatile: Boolean): Boolean =
  83
+    tree match {
  84
+      // Super is not technically a path.
  85
+      // However, syntactically, it can only occur nested in a Select.
  86
+      // This gives a nicer definition of isStableIdentifier that's equivalent to the spec's.
  87
+      // must consider Literal(_) a path for typedSingletonTypeTree
  88
+      case EmptyTree | Literal(_) => true
  89
+      case This(_) | Super(_, _)  => symOk(tree.symbol)
  90
+      case _                      => isStableIdentifier(tree, allowVolatile)
  91
+    }
  92
+
  93
+  /** Is `tree` a stable identifier, a path which ends in an identifier?
  94
+   *
  95
+   * StableId ::= id
  96
+   *           | Path ‘.’ id
  97
+   *           | [id ’.’] ‘super’ [‘[’ id ‘]’] ‘.’ id
  98
+   */
  99
+  def isStableIdentifier(tree: Tree, allowVolatile: Boolean): Boolean =
  100
+    tree match {
  101
+      case Ident(_)        => symOk(tree.symbol) && tree.symbol.isStable && !tree.symbol.hasVolatileType // TODO SPEC: not required by spec
  102
+      case Select(qual, _) => isStableMemberOf(tree.symbol, qual, allowVolatile) && isPath(qual, allowVolatile)
  103
+      case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX =>
  104
+        // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm`
  105
+        free.symbol.hasStableFlag && isPath(free, allowVolatile)
  106
+      case _               => false
  107
+    }
  108
+
  109
+  private def symOk(sym: Symbol) = sym != null && !sym.isError && sym != NoSymbol
  110
+  private def typeOk(tp: Type)   =  tp != null && ! tp.isError
  111
+
  112
+  /** Assuming `sym` is a member of `tree`, is it a "stable member"?
  113
+   *
  114
+   * Stable members are packages or members introduced
  115
+   * by object definitions or by value definitions of non-volatile types (§3.6).
  116
+   */
  117
+  def isStableMemberOf(sym: Symbol, tree: Tree, allowVolatile: Boolean): Boolean = (
  118
+    symOk(sym)       && (!sym.isTerm   || (sym.isStable && (allowVolatile || !sym.hasVolatileType))) &&
  119
+    typeOk(tree.tpe) && (allowVolatile || !hasVolatileType(tree)) && !definitions.isByNameParamType(tree.tpe)
  120
+  )
  121
+
  122
+  /** Is `tree`'s type volatile? (Ignored if its symbol has the @uncheckedStable annotation.)
  123
+   */
  124
+  def hasVolatileType(tree: Tree): Boolean =
  125
+    symOk(tree.symbol) && tree.tpe.isVolatile && !tree.symbol.hasAnnotation(uncheckedStableClass)
  126
+
  127
+  /** Is `tree` either a non-volatile type,
  128
+   *  or a path that does not include any of:
  129
+   *   - a reference to a mutable variable/field
  130
+   *   - a reference to a by-name parameter
  131
+   *   - a member selection on a volatile type (Spec: 3.6 Volatile Types)?
  132
+   *
  133
+   * Such a tree is a suitable target for type selection.
  134
+   */
  135
+  def admitsTypeSelection(tree: Tree): Boolean = isPath(tree, allowVolatile = false)
  136
+
  137
+  /** Is `tree` admissible as a stable identifier pattern (8.1.5 Stable Identifier Patterns)?
  138
+   *
  139
+   * We disregard volatility, as it's irrelevant in patterns (SI-6815)
  140
+   */
  141
+  def isStableIdentifierPattern(tree: Tree): Boolean = isStableIdentifier(tree, allowVolatile = true)
  142
+
69 143
   // TODO SI-5304 tighten this up so we don't elide side effect in module loads
70 144
   def isQualifierSafeToElide(tree: Tree): Boolean = isExprSafeToInline(tree)
71 145
 
@@ -473,9 +547,9 @@ abstract class TreeInfo {
473 547
   /** Does this CaseDef catch Throwable? */
474 548
   def catchesThrowable(cdef: CaseDef) = (
475 549
     cdef.guard.isEmpty && (unbind(cdef.pat) match {
476  
-      case Ident(nme.WILDCARD)       => true
477  
-      case i@Ident(name)             => hasNoSymbol(i)
478  
-      case _                         => false
  550
+      case Ident(nme.WILDCARD) => true
  551
+      case i@Ident(name)       => hasNoSymbol(i)
  552
+      case _                   => false
479 553
     })
480 554
   )
481 555
 
8  src/reflect/scala/reflect/internal/Types.scala
@@ -1079,7 +1079,7 @@ trait Types
1079 1079
                     (bcs eq bcs0) ||
1080 1080
                     (flags & PrivateLocal) != PrivateLocal ||
1081 1081
                     (bcs0.head.hasTransOwner(bcs.head)))) {
1082  
-                  if (name.isTypeName || stableOnly && sym.isStable) {
  1082
+                  if (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) {
1083 1083
                     if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start)
1084 1084
                     return sym
1085 1085
                   } else if (member eq NoSymbol) {
@@ -1356,7 +1356,7 @@ trait Types
1356 1356
     // more precise conceptually, but causes cyclic errors:    (paramss exists (_ contains sym))
1357 1357
     override def isImmediatelyDependent = (sym ne NoSymbol) && (sym.owner.isMethod && sym.isValueParameter)
1358 1358
 
1359  
-    override def isVolatile : Boolean = underlying.isVolatile && !sym.isStable
  1359
+    override def isVolatile : Boolean = underlying.isVolatile && (sym.hasVolatileType || !sym.isStable)
1360 1360
 /*
1361 1361
     override def narrow: Type = {
1362 1362
       if (phase.erasedTypes) this
@@ -3400,7 +3400,7 @@ trait Types
3400 3400
   /** Rebind symbol `sym` to an overriding member in type `pre`. */
3401 3401
   private def rebind(pre: Type, sym: Symbol): Symbol = {
3402 3402
     if (!sym.isOverridableMember || sym.owner == pre.typeSymbol) sym
3403  
-    else pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || sym.isStable) orElse sym
  3403
+    else pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || (sym.isStable && !sym.hasVolatileType)) orElse sym
3404 3404
   }
3405 3405
 
3406 3406
   /** Convert a `super` prefix to a this-type if `sym` is abstract or final. */
@@ -4096,7 +4096,7 @@ trait Types
4096 4096
     val info1 = tp1.memberInfo(sym1)
4097 4097
     val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1)
4098 4098
     //System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG
4099  
-    (    sym2.isTerm && isSubType(info1, info2, depth) && (!sym2.isStable || sym1.isStable)
  4099
+    (    sym2.isTerm && isSubType(info1, info2, depth) && (!sym2.isStable || sym1.isStable) && (!sym1.hasVolatileType || sym2.hasVolatileType)
4100 4100
       || sym2.isAbstractType && {
4101 4101
             val memberTp1 = tp1.memberType(sym1)
4102 4102
             // println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner)))
5  test/files/neg/t6815.check
... ...
@@ -0,0 +1,5 @@
  1
+t6815.scala:15: error: stable identifier required, but Test.this.u.emptyValDef found.
  2
+ Note that value emptyValDef is not stable because its type, Test.u.ValDef, is volatile.
  3
+    case _: u.emptyValDef.T => // and, unlike in pos/t6185.scala, we shouldn't allow this.
  4
+              ^
  5
+one error found
17  test/files/neg/t6815.scala
... ...
@@ -0,0 +1,17 @@
  1
+trait U {
  2
+  trait ValOrDefDefApi {
  3
+    def name: Any
  4
+  }
  5
+  type ValOrDefDef <: ValOrDefDefApi
  6
+  type ValDef <: ValOrDefDef with ValDefApi { type T }
  7
+  trait ValDefApi extends ValOrDefDefApi { this: ValDef => }
  8
+  val emptyValDef: ValDef // the result type is volatile
  9
+}
  10
+
  11
+object Test {
  12
+  val u: U = ???
  13
+
  14
+  (null: Any) match {
  15
+    case _: u.emptyValDef.T => // and, unlike in pos/t6185.scala, we shouldn't allow this.
  16
+  }
  17
+}
5  test/files/neg/volatile_no_override.check
... ...
@@ -0,0 +1,5 @@
  1
+volatile_no_override.scala:13: error: overriding value x in class A of type Volatile.this.D;
  2
+ value x has a volatile type; cannot override a member with non-volatile type
  3
+  val x: A with D = null
  4
+      ^
  5
+one error found
14  test/files/neg/volatile_no_override.scala
... ...
@@ -0,0 +1,14 @@
  1
+class B
  2
+class C(x: String) extends B
  3
+
  4
+abstract class A {
  5
+  class D { type T >: C <: B }
  6
+  val x: D
  7
+  var y: x.T = new C("abc")
  8
+}
  9
+
  10
+class Volatile extends A {
  11
+  type A >: Null
  12
+  // test (1.4), pt 2 in RefChecks
  13
+  val x: A with D = null
  14
+}
17  test/files/pos/t6815.scala
... ...
@@ -0,0 +1,17 @@
  1
+trait U {
  2
+  trait ValOrDefDefApi {
  3
+    def name: Any
  4
+  }
  5
+  type ValOrDefDef <: ValOrDefDefApi
  6
+  type ValDef <: ValOrDefDef with ValDefApi
  7
+  trait ValDefApi extends ValOrDefDefApi { this: ValDef => }
  8
+  val emptyValDef: ValDef // the result type is volatile
  9
+}
  10
+
  11
+object Test {
  12
+  val u: U = ???
  13
+
  14
+  u.emptyValDef match {
  15
+    case u.emptyValDef => // but we shouldn't let that stop us from treating it as a stable identifier pattern.
  16
+  }
  17
+}
16  test/files/pos/t6815_import.scala
... ...
@@ -0,0 +1,16 @@
  1
+trait U {
  2
+  trait ValOrDefDefApi {
  3
+    def name: Any
  4
+  }
  5
+  type ValOrDefDef <: ValOrDefDefApi
  6
+  type ValDef <: ValOrDefDef with ValDefApi
  7
+  trait ValDefApi extends ValOrDefDefApi { this: ValDef => }
  8
+  val emptyValDef: ValDef // the result type is volatile
  9
+}
  10
+
  11
+object Test {
  12
+  val u: U = ???
  13
+
  14
+  // but we shouldn't let that stop us from treating it as a stable identifier for import
  15
+  import u.emptyValDef.name
  16
+}

0 notes on commit fada1ef

Please sign in to comment.
Something went wrong with that request. Please try again.