aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-03-09 13:35:33 +0100
committerMartin Odersky <odersky@gmail.com>2014-03-09 13:39:22 +0100
commitaf337f0667fa559411f9a96355b9ceeabf95b232 (patch)
tree25d3e9e7c55b9adcce5e744108f0ff219bb7b874 /src/dotty/tools/dotc/core
parentd827b0180b05e7461856f668c3c4ca0ea6ed5d62 (diff)
downloaddotty-af337f0667fa559411f9a96355b9ceeabf95b232.tar.gz
dotty-af337f0667fa559411f9a96355b9ceeabf95b232.tar.bz2
dotty-af337f0667fa559411f9a96355b9ceeabf95b232.zip
Fix of #50 - volatile
Volatile checking needs to take all intersections into account; previously these could be discarded through needsChecking. Plus several refactorings and additions. 1) Module vals now have Final and Stable flags set 2) All logic around isVolatile is now in TypeOps; some of it was moved from Types. 3) Added stability checking to Select and SelectFromType typings. Todo: We should find a better name for isVolatile. Maybe define the negation instead under the name "isRealizable"?.
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala72
-rw-r--r--src/dotty/tools/dotc/core/Types.scala47
4 files changed, 59 insertions, 64 deletions
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index 0678b79be..d150bd6ab 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -422,7 +422,7 @@ object Flags {
final val RetainedTypeArgFlags = VarianceFlags | ExpandedName | Protected | Local
/** Modules always have these flags set */
- final val ModuleCreationFlags = ModuleVal
+ final val ModuleCreationFlags = ModuleVal | Final | Stable
/** Module classes always have these flags set */
final val ModuleClassCreationFlags = ModuleClass | Final
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 5fbf6c2c4..b9eb2f945 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -355,7 +355,7 @@ object SymDenotations {
final def isStable(implicit ctx: Context) = {
val isUnstable =
(this is UnstableValue) ||
- info.isVolatile && !hasAnnotation(defn.uncheckedStableClass)
+ ctx.isVolatile(info) && !hasAnnotation(defn.uncheckedStableClass)
(this is Stable) || isType || {
if (isUnstable) false
else { setFlag(Stable); true }
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index ca69ab615..15e3021a2 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -3,6 +3,8 @@ package core
import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
import SymDenotations._
+import config.Printers._
+import Decorators._
import util.SimpleMap
trait TypeOps { this: Context =>
@@ -75,7 +77,12 @@ trait TypeOps { this: Context =>
}
final def isVolatile(tp: Type): Boolean = {
- /** Pre-filter to avoid expensive DNF computation */
+
+ /** Pre-filter to avoid expensive DNF computation
+ * If needsChecking returns false it is guaranteed that
+ * DNF does not contain intersections, or abstract types with upper
+ * bounds that themselves need checking.
+ */
def needsChecking(tp: Type, isPart: Boolean): Boolean = tp match {
case tp: TypeRef =>
tp.info match {
@@ -88,31 +95,62 @@ trait TypeOps { this: Context =>
needsChecking(tp.parent, true)
case tp: TypeProxy =>
needsChecking(tp.underlying, isPart)
- case AndType(l, r) =>
- needsChecking(l, true) || needsChecking(r, true)
- case OrType(l, r) =>
- isPart || needsChecking(l, isPart) && needsChecking(r, isPart)
+ case tp: AndType =>
+ true
+ case tp: OrType =>
+ isPart || needsChecking(tp.tp1, isPart) && needsChecking(tp.tp2, isPart)
case _ =>
false
}
+
needsChecking(tp, false) && {
- tp.DNF forall { case (parents, refinedNames) =>
+ DNF(tp) forall { case (parents, refinedNames) =>
val absParents = parents filter (_.symbol is Deferred)
- absParents.size >= 2 || {
- val ap = absParents.head
- ((parents exists (p =>
- (p ne ap)
- || p.memberNames(abstractTypeNameFilter, tp).nonEmpty
- || p.memberNames(abstractTermNameFilter, tp).nonEmpty))
- || (refinedNames & tp.memberNames(abstractTypeNameFilter, tp)).nonEmpty
- || (refinedNames & tp.memberNames(abstractTermNameFilter, tp)).nonEmpty
- || isVolatile(ap)
- )
+ absParents.nonEmpty && {
+ absParents.lengthCompare(2) >= 0 || {
+ val ap = absParents.head
+ ((parents exists (p =>
+ (p ne ap)
+ || p.memberNames(abstractTypeNameFilter, tp).nonEmpty
+ || p.memberNames(abstractTermNameFilter, tp).nonEmpty))
+ || (refinedNames & tp.memberNames(abstractTypeNameFilter, tp)).nonEmpty
+ || (refinedNames & tp.memberNames(abstractTermNameFilter, tp)).nonEmpty
+ || isVolatile(ap))
+ }
}
}
}
}
+ /** The disjunctive normal form of this type.
+ * This collects a set of alternatives, each alternative consisting
+ * of a set of typerefs and a set of refinement names. Both sets are represented
+ * as lists, to obtain a deterministic order. Collected are
+ * all type refs reachable by following aliases and type proxies, and
+ * collecting the elements of conjunctions (&) and disjunctions (|).
+ * The set of refinement names in each alternative
+ * are the set of names in refinement types encountered during the collection.
+ */
+ final def DNF(tp: Type): List[(List[TypeRef], Set[Name])] = ctx.traceIndented(s"DNF($this)", checks) {
+ tp.dealias match {
+ case tp: TypeRef =>
+ (tp :: Nil, Set[Name]()) :: Nil
+ case RefinedType(parent, name) =>
+ for ((ps, rs) <- DNF(parent)) yield (ps, rs + name)
+ case tp: TypeProxy =>
+ DNF(tp.underlying)
+ case AndType(l, r) =>
+ for ((lps, lrs) <- DNF(l); (rps, rrs) <- DNF(r))
+ yield (lps | rps, lrs | rrs)
+ case OrType(l, r) =>
+ DNF(l) | DNF(r)
+ case tp =>
+ TypeOps.emptyDNF
+ }
+ }
+
+
+
private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = {
val lazyInfo = new LazyType { // needed so we do not force `formal`.
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
@@ -215,6 +253,6 @@ trait TypeOps { this: Context =>
}
object TypeOps {
-
+ val emptyDNF = (Nil, Set[Name]()) :: Nil
var track = false // !!!DEBUG
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index bb30d9a9c..67dc13775 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -106,7 +106,8 @@ object Types {
classSymbol.derivesFrom(cls)
/** A type T is a legal prefix in a type selection T#A if
- * T is stable or T contains no uninstantiated type variables.
+ * T is stable or T contains no abstract types
+ * !!! Todo: What about non-final vals that contain abstract types?
*/
final def isLegalPrefix(implicit ctx: Context): Boolean =
isStable || memberNames(abstractTypeNameFilter).isEmpty
@@ -127,25 +128,6 @@ object Types {
/** Is some part of this type produced as a repair for an error? */
final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError)
- /** A type is volatile if its DNF contains an alternative of the form
- * {P1, ..., Pn}, {N1, ..., Nk}, where the Pi are parent typerefs and the
- * Nj are refinement names, and one the 4 following conditions is met:
- *
- * 1. At least two of the parents Pi are abstract types.
- * 2. One of the parents Pi is an abstract type, and one other type Pj,
- * j != i has an abstract member which has the same name as an
- * abstract member of the whole type.
- * 3. One of the parents Pi is an abstract type, and one of the refinement
- * names Nj refers to an abstract member of the whole type.
- * 4. One of the parents Pi is an abstract type with a volatile upper bound.
- *
- * Lazy values are not allowed to have volatile type, as otherwise
- * unsoundness can result.
- */
- final def isVolatile(implicit ctx: Context): Boolean = track("isVolatile") {
- ctx.isVolatile(this)
- }
-
/** Does the type carry an annotation that is an instance of `cls`? */
final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match {
case AnnotatedType(annot, tp) => (annot matches cls) || (tp hasAnnotation cls)
@@ -741,31 +723,6 @@ object Types {
def typeParamNamed(name: TypeName)(implicit ctx: Context): Symbol =
classSymbol.decls.lookup(name) orElse member(name).symbol
- /** The disjunctive normal form of this type.
- * This collects a set of alternatives, each alternative consisting
- * of a set of typerefs and a set of refinement names. Both sets are represented
- * as lists, to obtain a deterministic order. Collected are
- * all type refs reachable by following aliases and type proxies, and
- * collecting the elements of conjunctions (&) and disjunctions (|).
- * The set of refinement names in each alternative
- * are the set of names in refinement types encountered during the collection.
- */
- final def DNF(implicit ctx: Context): List[(List[TypeRef], Set[Name])] = dealias match {
- case tp: TypeRef =>
- (tp :: Nil, Set[Name]()) :: Nil
- case RefinedType(parent, name) =>
- for ((ps, rs) <- parent.DNF) yield (ps, rs + name)
- case tp: TypeProxy =>
- tp.underlying.DNF
- case AndType(l, r) =>
- for ((lps, lrs) <- l.DNF; (rps, rrs) <- r.DNF)
- yield (lps | rps, lrs | rrs)
- case OrType(l, r) =>
- l.DNF | r.DNF
- case tp =>
- emptyDNF
- }
-
// ----- Substitutions -----------------------------------------------------
/** Substitute all types that refer in their symbol attribute to