summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/TreeInfo.scala
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-05-16 14:58:09 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-05-16 15:00:35 -0700
commitfada1ef6b315326ac0329d9e78951cfc95ad0eb0 (patch)
treea29fb94694bb033c33bfe7e8ec3be9534101fb53 /src/reflect/scala/reflect/internal/TreeInfo.scala
parent97d5179127e02af39b19076e78e4b2bc099eef94 (diff)
downloadscala-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.scala82
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
})
)