From 97d5179127e02af39b19076e78e4b2bc099eef94 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 16 May 2013 14:57:13 -0700 Subject: make treeInfo more globally visible --- src/reflect/scala/reflect/internal/SymbolTable.scala | 2 ++ src/reflect/scala/reflect/internal/Symbols.scala | 2 ++ src/reflect/scala/reflect/runtime/JavaUniverse.scala | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 01cf0dd8d7..7f9811220f 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -126,6 +126,8 @@ abstract class SymbolTable extends macros.Universe val global: SymbolTable.this.type = SymbolTable.this } with util.TraceSymbolActivity + val treeInfo: TreeInfo { val global: SymbolTable.this.type } + /** Check that the executing thread is the compiler thread. No-op here, * overridden in interactive.Global. */ @elidable(elidable.WARNING) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 9c706f650e..7807ee226b 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -92,6 +92,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isExistential: Boolean = this.isExistentiallyBound def isParamWithDefault: Boolean = this.hasDefault + // `isByNameParam` is only true for a call-by-name parameter of a *method*, + // an argument of the primary constructor seen in the class body is excluded by `isValueParameter` def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def isJava: Boolean = isJavaDefined diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index 4805c36ce8..06a7db6289 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -21,5 +21,10 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S def newStrictTreeCopier: TreeCopier = new StrictTreeCopier def newLazyTreeCopier: TreeCopier = new LazyTreeCopier + // can't put this in runtime.Trees since that's mixed with Global in ReflectGlobal, which has the definition from internal.Trees + object treeInfo extends { + val global: JavaUniverse.this.type = JavaUniverse.this + } with internal.TreeInfo + init() } -- cgit v1.2.3 From fada1ef6b315326ac0329d9e78951cfc95ad0eb0 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 16 May 2013 14:58:09 -0700 Subject: 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. --- .../tools/nsc/typechecker/ContextErrors.scala | 2 +- .../scala/tools/nsc/typechecker/Namers.scala | 5 +- .../tools/nsc/typechecker/NamesDefaults.scala | 2 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 5 +- .../scala/tools/nsc/typechecker/Typers.scala | 47 ++++++++----- .../scala/tools/nsc/interactive/Global.scala | 6 +- .../scala/tools/nsc/interactive/Picklers.scala | 2 +- src/reflect/scala/reflect/internal/Symbols.scala | 16 ++--- src/reflect/scala/reflect/internal/TreeGen.scala | 20 +++--- src/reflect/scala/reflect/internal/TreeInfo.scala | 82 ++++++++++++++++++++-- src/reflect/scala/reflect/internal/Types.scala | 8 +-- test/files/neg/t6815.check | 5 ++ test/files/neg/t6815.scala | 17 +++++ test/files/neg/volatile_no_override.check | 5 ++ test/files/neg/volatile_no_override.scala | 14 ++++ test/files/pos/t6815.scala | 17 +++++ test/files/pos/t6815_import.scala | 16 +++++ 17 files changed, 215 insertions(+), 54 deletions(-) create mode 100644 test/files/neg/t6815.check create mode 100644 test/files/neg/t6815.scala create mode 100644 test/files/neg/volatile_no_override.check create mode 100644 test/files/neg/volatile_no_override.scala create mode 100644 test/files/pos/t6815.scala create mode 100644 test/files/pos/t6815_import.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 61cdb63ac9..e1e26cd8c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -141,7 +141,7 @@ trait ContextErrors { } issueNormalTypeError(tree, "stable identifier required, but "+tree+" found." + ( - if (isStableExceptVolatile(tree)) addendum else "")) + if (treeInfo.hasVolatileType(tree)) addendum else "")) setError(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 541d60c16d..55f144da13 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1342,13 +1342,16 @@ trait Namers extends MethodSynthesis { private def importSig(imp: Import) = { val Import(expr, selectors) = imp val expr1 = typer.typedQualifier(expr) - typer checkStable expr1 + if (expr1.symbol != null && expr1.symbol.isRootPackage) RootImportError(imp) if (expr1.isErrorTyped) ErrorType else { + if (!treeInfo.isStableIdentifierPattern(expr1)) + typer.TyperErrorGen.UnstableTreeError(expr1) + val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import] checkSelectors(newImport) transformed(imp) = newImport diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index c08aa7e39f..ecbc471fbe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -202,7 +202,7 @@ trait NamesDefaults { self: Analyzer => if (module == NoSymbol) None else { val ref = atPos(pos.focus)(gen.mkAttributedRef(pre, module)) - if (module.isStable && pre.isStable) // fixes #4524. the type checker does the same for + if (treeInfo.admitsTypeSelection(ref)) // fixes #4524. the type checker does the same for ref.setType(singleType(pre, module)) // typedSelect, it calls "stabilize" on the result. Some(ref) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 290ad76a1d..bbba786c0c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -511,7 +511,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } if (member.isStable && !otherTp.isVolatile) { - if (memberTp.isVolatile) + // (1.4), pt 2 -- member.isStable && memberTp.isVolatile started being possible after SI-6815 + // (before SI-6815, !symbol.tpe.isVolatile was implied by symbol.isStable) + // TODO: allow overriding when @uncheckedStable? + if (memberTp.isVolatile) overrideError("has a volatile type; cannot override a member with non-volatile type") else memberTp.dealiasWiden.resultType match { case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2de59056ef..45f67a4858 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -587,7 +587,7 @@ trait Typers extends Adaptations with Tags { } /** Post-process an identifier or selection node, performing the following: - * 1. Check that non-function pattern expressions are stable + * 1. Check that non-function pattern expressions are stable (ignoring volatility concerns -- SI-6815) * 2. Check that packages and static modules are not used as values * 3. Turn tree type into stable type if possible and required by context. * 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 { if (tree.isErrorTyped) tree else if (mode.inPatternNotFunMode && tree.isTerm) { // (1) if (sym.isValue) { - val tree1 = checkStable(tree) - // A module reference in a pattern has type Foo.type, not "object Foo" - if (sym.isModuleNotMethod) tree1 setType singleType(pre, sym) - else tree1 + if (tree.isErrorTyped) tree + else if (treeInfo.isStableIdentifierPattern(tree)) { + // A module reference in a pattern has type Foo.type, not "object Foo" + if (sym.isModuleNotMethod) tree setType singleType(pre, sym) + else tree + } else UnstableTreeError(tree) } else fail() } else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2) fail() } else { - if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) && + if (treeInfo.admitsTypeSelection(tree) && (isStableContext(tree, mode, pt) || sym.isModuleNotMethod)) tree.setType(singleType(pre, sym)) // To fully benefit from special casing the return type of @@ -4442,6 +4444,7 @@ trait Typers extends Adaptations with Tags { } def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { + // TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)` val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable val funpt = if (isPatternMode) pt else WildcardType val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null @@ -4744,16 +4747,20 @@ trait Typers extends Adaptations with Tags { typedSelect(tree, qual1, nme.CONSTRUCTOR) case _ => if (Statistics.canEnable) Statistics.incCounter(typedSelectCount) - var qual1 = checkDead(typedQualifier(qual, mode)) - if (name.isTypeName) qual1 = checkStable(qual1) + val qualTyped = checkDead(typedQualifier(qual, mode)) + val qualStableOrError = + if (qualTyped.isErrorTyped || !name.isTypeName || treeInfo.admitsTypeSelection(qualTyped)) + qualTyped + else + UnstableTreeError(qualTyped) val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) - silent(_ => typedSelect(tree, qual1, name)) orElse { _ => - silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { + silent(_ => typedSelect(tree, qualStableOrError, name)) orElse { _ => + silent(_ => typed1(Select(qualStableOrError, nme.filter) setPos tree.pos, mode, pt)) match { case SilentResultValue(result2) => unit.deprecationWarning( - tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen + + tree.pos, "`withFilter' method does not yet exist on " + qualStableOrError.tpe.widen + ", using `filter' method instead") result2 case SilentTypeError(err) => @@ -4761,14 +4768,14 @@ trait Typers extends Adaptations with Tags { } } else - typedSelect(tree, qual1, name) + typedSelect(tree, qualStableOrError, name) if (tree.isInstanceOf[PostfixSelect]) checkFeature(tree.pos, PostfixOpsFeature, name.decode) if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) - if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) + if (qualStableOrError.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) else tree1 } } @@ -5161,12 +5168,16 @@ trait Typers extends Adaptations with Tags { } def typedSingletonTypeTree(tree: SingletonTypeTree) = { - val ref1 = checkStable( - context.withImplicitsDisabled( + val refTyped = + context.withImplicitsDisabled { typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe) - ) - ) - tree setType ref1.tpe.resultType + } + + if (!refTyped.isErrorTyped) + tree setType refTyped.tpe.resultType + + if (treeInfo.admitsTypeSelection(refTyped)) tree + else UnstableTreeError(refTyped) } def typedSelectFromTypeTree(tree: SelectFromTypeTree) = { diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index c9b4603d74..6a833ab864 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -925,14 +925,14 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } def stabilizedType(tree: Tree): Type = tree match { - case Ident(_) if tree.symbol.isStable => + case Ident(_) if treeInfo.admitsTypeSelection(tree) => singleType(NoPrefix, tree.symbol) - case Select(qual, _) if qual.tpe != null && tree.symbol.isStable => + case Select(qual, _) if treeInfo.admitsTypeSelection(tree) => singleType(qual.tpe, tree.symbol) case Import(expr, selectors) => tree.symbol.info match { case analyzer.ImportType(expr) => expr match { - case s@Select(qual, name) => singleType(qual.tpe, s.symbol) + case s@Select(qual, name) if treeInfo.admitsTypeSelection(expr) => singleType(qual.tpe, s.symbol) case i : Ident => i.tpe case _ => tree.tpe } diff --git a/src/interactive/scala/tools/nsc/interactive/Picklers.scala b/src/interactive/scala/tools/nsc/interactive/Picklers.scala index b184afd0f5..900a06333d 100644 --- a/src/interactive/scala/tools/nsc/interactive/Picklers.scala +++ b/src/interactive/scala/tools/nsc/interactive/Picklers.scala @@ -96,7 +96,7 @@ trait Picklers { self: Global => if (!sym.isRoot) { ownerNames(sym.owner, buf) buf += (if (sym.isModuleClass) sym.sourceModule else sym).name - if (!sym.isType && !sym.isStable) { + if (!sym.isType && !sym.isStable) { // TODO: what's the reasoning behind this condition!? val sym1 = sym.owner.info.decl(sym.name) if (sym1.isOverloaded) { val index = sym1.alternatives.indexOf(sym) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 7807ee226b..cf4e210ccf 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -790,8 +790,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Is this symbol an accessor method for outer? */ final def isOuterField = isArtifact && (unexpandedName == nme.OUTER_LOCAL) - /** Does this symbol denote a stable value? */ - def isStable = false + /** Does this symbol denote a stable value, ignoring volatility? + * + * Stability and volatility are checked separately to allow volatile paths in patterns that amount to equality checks. SI-6815 + */ + final def isStable = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag) + final def hasVolatileType = tpe.isVolatile && !hasAnnotation(uncheckedStableClass) /** Does this symbol denote the primary constructor of its enclosing class? */ final def isPrimaryConstructor = @@ -2589,13 +2593,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isMixinConstructor = name == nme.MIXIN_CONSTRUCTOR override def isConstructor = nme.isConstructorName(name) - override def isPackageObject = isModule && (name == nme.PACKAGE) - override def isStable = !isUnstable - private def isUnstable = ( - isMutable - || (hasFlag(METHOD | BYNAMEPARAM) && !hasFlag(STABLE)) - || (tpe.isVolatile && !hasAnnotation(uncheckedStableClass)) - ) + override def isPackageObject = isModule && (name == nme.PACKAGE) // The name in comments is what it is being disambiguated from. // TODO - rescue CAPTURED from BYNAMEPARAM so we can see all the names. diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index d25189516e..44c66b2bbf 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -142,17 +142,15 @@ abstract class TreeGen extends macros.TreeBuilder { } /** Computes stable type for a tree if possible */ - def stableTypeFor(tree: Tree): Option[Type] = tree match { - case This(_) if tree.symbol != null && !tree.symbol.isError => - Some(ThisType(tree.symbol)) - case Ident(_) if tree.symbol.isStable => - Some(singleType(tree.symbol.owner.thisType, tree.symbol)) - case Select(qual, _) if ((tree.symbol ne null) && (qual.tpe ne null)) && // turned assert into guard for #4064 - tree.symbol.isStable && qual.tpe.isStable => - Some(singleType(qual.tpe, tree.symbol)) - case _ => - None - } + def stableTypeFor(tree: Tree): Option[Type] = + if (treeInfo.admitsTypeSelection(tree)) + tree match { + case This(_) => Some(ThisType(tree.symbol)) + case Ident(_) => Some(singleType(tree.symbol.owner.thisType, tree.symbol)) + case Select(qual, _) => Some(singleType(qual.tpe, tree.symbol)) + case _ => None + } + else None /** Builds a reference with stable type to given symbol */ def mkAttributedStableRef(pre: Type, sym: Symbol): Tree = diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 2f1b3208df..4c0f0695b3 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -18,7 +18,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } + import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType, uncheckedStableClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -66,6 +66,80 @@ abstract class TreeInfo { false } + /** Is `tree` a path, defined as follows? (Spec: 3.1 Paths) + * + * - The empty path ε (which cannot be written explicitly in user programs). + * - C.this, where C references a class. + * - p.x where p is a path and x is a stable member of p. + * - C.super.x or C.super[M].x where C references a class + * and x references a stable member of the super class or designated parent class M of C. + * + * NOTE: Trees with errors are (mostly) excluded. + * + * Path ::= StableId | [id ‘.’] this + * + */ + def isPath(tree: Tree, allowVolatile: Boolean): Boolean = + tree match { + // Super is not technically a path. + // However, syntactically, it can only occur nested in a Select. + // This gives a nicer definition of isStableIdentifier that's equivalent to the spec's. + // must consider Literal(_) a path for typedSingletonTypeTree + case EmptyTree | Literal(_) => true + case This(_) | Super(_, _) => symOk(tree.symbol) + case _ => isStableIdentifier(tree, allowVolatile) + } + + /** Is `tree` a stable identifier, a path which ends in an identifier? + * + * StableId ::= id + * | Path ‘.’ id + * | [id ’.’] ‘super’ [‘[’ id ‘]’] ‘.’ id + */ + def isStableIdentifier(tree: Tree, allowVolatile: Boolean): Boolean = + tree match { + case Ident(_) => symOk(tree.symbol) && tree.symbol.isStable && !tree.symbol.hasVolatileType // TODO SPEC: not required by spec + case Select(qual, _) => isStableMemberOf(tree.symbol, qual, allowVolatile) && isPath(qual, allowVolatile) + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` + free.symbol.hasStableFlag && isPath(free, allowVolatile) + case _ => false + } + + private def symOk(sym: Symbol) = sym != null && !sym.isError && sym != NoSymbol + private def typeOk(tp: Type) = tp != null && ! tp.isError + + /** Assuming `sym` is a member of `tree`, is it a "stable member"? + * + * Stable members are packages or members introduced + * by object definitions or by value definitions of non-volatile types (§3.6). + */ + def isStableMemberOf(sym: Symbol, tree: Tree, allowVolatile: Boolean): Boolean = ( + symOk(sym) && (!sym.isTerm || (sym.isStable && (allowVolatile || !sym.hasVolatileType))) && + typeOk(tree.tpe) && (allowVolatile || !hasVolatileType(tree)) && !definitions.isByNameParamType(tree.tpe) + ) + + /** Is `tree`'s type volatile? (Ignored if its symbol has the @uncheckedStable annotation.) + */ + def hasVolatileType(tree: Tree): Boolean = + symOk(tree.symbol) && tree.tpe.isVolatile && !tree.symbol.hasAnnotation(uncheckedStableClass) + + /** Is `tree` either a non-volatile type, + * or a path that does not include any of: + * - a reference to a mutable variable/field + * - a reference to a by-name parameter + * - a member selection on a volatile type (Spec: 3.6 Volatile Types)? + * + * Such a tree is a suitable target for type selection. + */ + def admitsTypeSelection(tree: Tree): Boolean = isPath(tree, allowVolatile = false) + + /** Is `tree` admissible as a stable identifier pattern (8.1.5 Stable Identifier Patterns)? + * + * We disregard volatility, as it's irrelevant in patterns (SI-6815) + */ + def isStableIdentifierPattern(tree: Tree): Boolean = isStableIdentifier(tree, allowVolatile = true) + // TODO SI-5304 tighten this up so we don't elide side effect in module loads def isQualifierSafeToElide(tree: Tree): Boolean = isExprSafeToInline(tree) @@ -473,9 +547,9 @@ abstract class TreeInfo { /** Does this CaseDef catch Throwable? */ def catchesThrowable(cdef: CaseDef) = ( cdef.guard.isEmpty && (unbind(cdef.pat) match { - case Ident(nme.WILDCARD) => true - case i@Ident(name) => hasNoSymbol(i) - case _ => false + case Ident(nme.WILDCARD) => true + case i@Ident(name) => hasNoSymbol(i) + case _ => false }) ) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index a8fc55e677..00fd20a46b 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1079,7 +1079,7 @@ trait Types (bcs eq bcs0) || (flags & PrivateLocal) != PrivateLocal || (bcs0.head.hasTransOwner(bcs.head)))) { - if (name.isTypeName || stableOnly && sym.isStable) { + if (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) { if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) return sym } else if (member eq NoSymbol) { @@ -1356,7 +1356,7 @@ trait Types // more precise conceptually, but causes cyclic errors: (paramss exists (_ contains sym)) override def isImmediatelyDependent = (sym ne NoSymbol) && (sym.owner.isMethod && sym.isValueParameter) - override def isVolatile : Boolean = underlying.isVolatile && !sym.isStable + override def isVolatile : Boolean = underlying.isVolatile && (sym.hasVolatileType || !sym.isStable) /* override def narrow: Type = { if (phase.erasedTypes) this @@ -3400,7 +3400,7 @@ trait Types /** Rebind symbol `sym` to an overriding member in type `pre`. */ private def rebind(pre: Type, sym: Symbol): Symbol = { if (!sym.isOverridableMember || sym.owner == pre.typeSymbol) sym - else pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || sym.isStable) orElse sym + else pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || (sym.isStable && !sym.hasVolatileType)) orElse sym } /** Convert a `super` prefix to a this-type if `sym` is abstract or final. */ @@ -4096,7 +4096,7 @@ trait Types val info1 = tp1.memberInfo(sym1) val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1) //System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG - ( sym2.isTerm && isSubType(info1, info2, depth) && (!sym2.isStable || sym1.isStable) + ( sym2.isTerm && isSubType(info1, info2, depth) && (!sym2.isStable || sym1.isStable) && (!sym1.hasVolatileType || sym2.hasVolatileType) || sym2.isAbstractType && { val memberTp1 = tp1.memberType(sym1) // println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner))) diff --git a/test/files/neg/t6815.check b/test/files/neg/t6815.check new file mode 100644 index 0000000000..fae3819be1 --- /dev/null +++ b/test/files/neg/t6815.check @@ -0,0 +1,5 @@ +t6815.scala:15: error: stable identifier required, but Test.this.u.emptyValDef found. + Note that value emptyValDef is not stable because its type, Test.u.ValDef, is volatile. + case _: u.emptyValDef.T => // and, unlike in pos/t6185.scala, we shouldn't allow this. + ^ +one error found diff --git a/test/files/neg/t6815.scala b/test/files/neg/t6815.scala new file mode 100644 index 0000000000..ff973a7437 --- /dev/null +++ b/test/files/neg/t6815.scala @@ -0,0 +1,17 @@ +trait U { + trait ValOrDefDefApi { + def name: Any + } + type ValOrDefDef <: ValOrDefDefApi + type ValDef <: ValOrDefDef with ValDefApi { type T } + trait ValDefApi extends ValOrDefDefApi { this: ValDef => } + val emptyValDef: ValDef // the result type is volatile +} + +object Test { + val u: U = ??? + + (null: Any) match { + case _: u.emptyValDef.T => // and, unlike in pos/t6185.scala, we shouldn't allow this. + } +} diff --git a/test/files/neg/volatile_no_override.check b/test/files/neg/volatile_no_override.check new file mode 100644 index 0000000000..a9a60ab697 --- /dev/null +++ b/test/files/neg/volatile_no_override.check @@ -0,0 +1,5 @@ +volatile_no_override.scala:13: error: overriding value x in class A of type Volatile.this.D; + value x has a volatile type; cannot override a member with non-volatile type + val x: A with D = null + ^ +one error found diff --git a/test/files/neg/volatile_no_override.scala b/test/files/neg/volatile_no_override.scala new file mode 100644 index 0000000000..9fad082a90 --- /dev/null +++ b/test/files/neg/volatile_no_override.scala @@ -0,0 +1,14 @@ +class B +class C(x: String) extends B + +abstract class A { + class D { type T >: C <: B } + val x: D + var y: x.T = new C("abc") +} + +class Volatile extends A { + type A >: Null + // test (1.4), pt 2 in RefChecks + val x: A with D = null +} diff --git a/test/files/pos/t6815.scala b/test/files/pos/t6815.scala new file mode 100644 index 0000000000..9244b3d353 --- /dev/null +++ b/test/files/pos/t6815.scala @@ -0,0 +1,17 @@ +trait U { + trait ValOrDefDefApi { + def name: Any + } + type ValOrDefDef <: ValOrDefDefApi + type ValDef <: ValOrDefDef with ValDefApi + trait ValDefApi extends ValOrDefDefApi { this: ValDef => } + val emptyValDef: ValDef // the result type is volatile +} + +object Test { + val u: U = ??? + + u.emptyValDef match { + case u.emptyValDef => // but we shouldn't let that stop us from treating it as a stable identifier pattern. + } +} diff --git a/test/files/pos/t6815_import.scala b/test/files/pos/t6815_import.scala new file mode 100644 index 0000000000..56f4358d59 --- /dev/null +++ b/test/files/pos/t6815_import.scala @@ -0,0 +1,16 @@ +trait U { + trait ValOrDefDefApi { + def name: Any + } + type ValOrDefDef <: ValOrDefDefApi + type ValDef <: ValOrDefDef with ValDefApi + trait ValDefApi extends ValOrDefDefApi { this: ValDef => } + val emptyValDef: ValDef // the result type is volatile +} + +object Test { + val u: U = ??? + + // but we shouldn't let that stop us from treating it as a stable identifier for import + import u.emptyValDef.name +} -- cgit v1.2.3 From 9a4361178f87d4540169422a1f9462aa2b539cb5 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 16 May 2013 15:00:54 -0700 Subject: remove unused methods: checkStable, isStableExceptVolatile --- .../scala/tools/nsc/typechecker/Typers.scala | 23 ---------------------- 1 file changed, 23 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 45f67a4858..6cf32a9858 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -250,29 +250,6 @@ trait Typers extends Adaptations with Tags { case _ => tp } - /** Check that `tree` is a stable expression. - */ - def checkStable(tree: Tree): Tree = ( - if (treeInfo.isExprSafeToInline(tree)) tree - else if (tree.isErrorTyped) tree - else UnstableTreeError(tree) - ) - - /** Would tree be a stable (i.e. a pure expression) if the type - * of its symbol was not volatile? - */ - protected def isStableExceptVolatile(tree: Tree) = { - tree.hasSymbolField && tree.symbol != NoSymbol && tree.tpe.isVolatile && - { val savedTpe = tree.symbol.info - val savedSTABLE = tree.symbol getFlag STABLE - tree.symbol setInfo AnyRefClass.tpe - tree.symbol setFlag STABLE - val result = treeInfo.isExprSafeToInline(tree) - tree.symbol setInfo savedTpe - tree.symbol setFlag savedSTABLE - result - } - } private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false } private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false } -- cgit v1.2.3