diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-05-16 14:58:09 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-05-16 15:00:35 -0700 |
commit | fada1ef6b315326ac0329d9e78951cfc95ad0eb0 (patch) | |
tree | a29fb94694bb033c33bfe7e8ec3be9534101fb53 /src/reflect/scala/reflect/internal/TreeInfo.scala | |
parent | 97d5179127e02af39b19076e78e4b2bc099eef94 (diff) | |
download | scala-fada1ef6b315326ac0329d9e78951cfc95ad0eb0.tar.gz scala-fada1ef6b315326ac0329d9e78951cfc95ad0eb0.tar.bz2 scala-fada1ef6b315326ac0329d9e78951cfc95ad0eb0.zip |
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.
Diffstat (limited to 'src/reflect/scala/reflect/internal/TreeInfo.scala')
-rw-r--r-- | src/reflect/scala/reflect/internal/TreeInfo.scala | 82 |
1 files changed, 78 insertions, 4 deletions
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 }) ) |