summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/Variances.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-12-31 12:36:04 -0800
committerPaul Phillips <paulp@improving.org>2013-01-09 12:11:13 -0800
commit91d8584fde15140972f7d6037d632bc24fc50f94 (patch)
treebe48d686376c24648cb9a27abd323ed63f94254a /src/reflect/scala/reflect/internal/Variances.scala
parent36ec5ff749a8148637129aa83d2cc597a773272b (diff)
downloadscala-91d8584fde15140972f7d6037d632bc24fc50f94.tar.gz
scala-91d8584fde15140972f7d6037d632bc24fc50f94.tar.bz2
scala-91d8584fde15140972f7d6037d632bc24fc50f94.zip
Moved Variances into SymbolTable.
So I can centralize all the redundant variance code.
Diffstat (limited to 'src/reflect/scala/reflect/internal/Variances.scala')
-rw-r--r--src/reflect/scala/reflect/internal/Variances.scala192
1 files changed, 192 insertions, 0 deletions
diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala
new file mode 100644
index 0000000000..6560f088c5
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/Variances.scala
@@ -0,0 +1,192 @@
+/* NSC -- new scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.reflect
+package internal
+
+import Variance._
+import scala.collection.{ mutable, immutable }
+
+/** See comments at scala.reflect.internal.Variance.
+ */
+trait Variances {
+ self: SymbolTable =>
+
+ /** Used in Refchecks.
+ * TODO - eliminate duplication with varianceInType
+ */
+ class VarianceValidator extends Traverser {
+ val escapedPrivateLocals = mutable.HashSet[Symbol]()
+
+ protected def issueVarianceError(base: Symbol, sym: Symbol, required: Variance): Unit = ()
+
+ /** Validate variance of info of symbol `base` */
+ private def validateVariance(base: Symbol) {
+ // A flag for when we're in a refinement, meaning method parameter types
+ // need to be checked.
+ var inRefinement = false
+
+ /** The variance of a symbol occurrence of `tvar`
+ * seen at the level of the definition of `base`.
+ * The search proceeds from `base` to the owner of `tvar`.
+ * Initially the state is covariant, but it might change along the search.
+ */
+ def relativeVariance(tvar: Symbol): Variance = {
+ val clazz = tvar.owner
+ var sym = base
+ var state: Variance = Covariant
+ while (sym != clazz && !state.isBivariant) {
+ //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG
+ // Flip occurrences of type parameters and parameters, unless
+ // - it's a constructor, or case class factory or extractor
+ // - it's a type parameter of tvar's owner.
+ if (sym.isParameter && !sym.owner.isConstructor && !sym.owner.isCaseApplyOrUnapply &&
+ !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem &&
+ tvar.owner == sym.owner)) state = state.flip
+ else if (!sym.owner.isClass ||
+ sym.isTerm && ((sym.isPrivateLocal || sym.isProtectedLocal || sym.isSuperAccessor /* super accessors are implicitly local #4345*/) && !(escapedPrivateLocals contains sym))) {
+ // return Bivariant if `sym` is local to a term
+ // or is private[this] or protected[this]
+ state = Bivariant
+ }
+ else if (sym.isAliasType) {
+ // return Bivariant if `sym` is an alias type
+ // that does not override anything. This is OK, because we always
+ // expand aliases for variance checking.
+ // However, if `sym` does override a type in a base class
+ // we have to assume Invariant, as there might then be
+ // references to the type parameter that are not variance checked.
+ state = if (sym.isOverridingSymbol) Invariant else Bivariant
+ }
+ sym = sym.owner
+ }
+ state
+ }
+
+ /** Validate that the type `tp` is variance-correct, assuming
+ * the type occurs itself at variance position given by `variance`
+ */
+ def validateVariance(tp: Type, variance: Variance): Unit = tp match {
+ case ErrorType =>
+ case WildcardType =>
+ case BoundedWildcardType(bounds) =>
+ validateVariance(bounds, variance)
+ case NoType =>
+ case NoPrefix =>
+ case ThisType(_) =>
+ case ConstantType(_) =>
+ case SingleType(pre, sym) =>
+ validateVariance(pre, variance)
+ case TypeRef(pre, sym, args) =>
+// println("validate "+sym+" at "+relativeVariance(sym))
+ if (sym.isAliasType/* && relativeVariance(sym) == Bivariant*/)
+ validateVariance(tp.normalize, variance)
+ else if (!sym.variance.isInvariant) {
+ val v = relativeVariance(sym)
+ val requiredVariance = v * variance
+
+ if (!v.isBivariant && sym.variance != requiredVariance)
+ issueVarianceError(base, sym, requiredVariance)
+ }
+ validateVariance(pre, variance)
+ // @M for higher-kinded typeref, args.isEmpty
+ // However, these args respect variances by construction anyway
+ // -- the interesting case is in type application, see checkKindBounds in Infer
+ if (args.nonEmpty)
+ validateVarianceArgs(args, variance, sym.typeParams)
+ case ClassInfoType(parents, decls, symbol) =>
+ validateVariances(parents, variance)
+ case RefinedType(parents, decls) =>
+ validateVariances(parents, variance)
+ val saved = inRefinement
+ inRefinement = true
+ for (sym <- decls)
+ validateVariance(sym.info, if (sym.isAliasType) Invariant else variance)
+ inRefinement = saved
+ case TypeBounds(lo, hi) =>
+ validateVariance(lo, variance.flip)
+ validateVariance(hi, variance)
+ case mt @ MethodType(formals, result) =>
+ if (inRefinement)
+ validateVariances(mt.paramTypes, variance.flip)
+ validateVariance(result, variance)
+ case NullaryMethodType(result) =>
+ validateVariance(result, variance)
+ case PolyType(tparams, result) =>
+ // type parameters will be validated separately, because they are defined explicitly.
+ validateVariance(result, variance)
+ case ExistentialType(tparams, result) =>
+ validateVariances(tparams map (_.info), variance)
+ validateVariance(result, variance)
+ case AnnotatedType(annots, tp, selfsym) =>
+ if (!annots.exists(_ matches definitions.uncheckedVarianceClass))
+ validateVariance(tp, variance)
+ }
+
+ def validateVariances(tps: List[Type], variance: Variance) {
+ tps foreach (tp => validateVariance(tp, variance))
+ }
+
+ def validateVarianceArgs(tps: List[Type], variance: Variance, tparams: List[Symbol]) {
+ foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance))
+ }
+
+ validateVariance(base.info, Covariant)
+ }
+
+ override def traverse(tree: Tree) {
+ tree match {
+ case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) =>
+ validateVariance(tree.symbol)
+ super.traverse(tree)
+ // ModuleDefs need not be considered because they have been eliminated already
+ case ValDef(_, _, _, _) =>
+ if (!tree.symbol.hasLocalFlag)
+ validateVariance(tree.symbol)
+ case DefDef(_, _, tparams, vparamss, _, _) =>
+ // No variance check for object-private/protected methods/values.
+ if (!tree.symbol.hasLocalFlag) {
+ validateVariance(tree.symbol)
+ traverseTrees(tparams)
+ traverseTreess(vparamss)
+ }
+ case Template(_, _, _) =>
+ super.traverse(tree)
+ case _ =>
+ }
+ }
+ }
+
+ /** Compute variance of type parameter `tparam` in all types `tps`. */
+ def varianceInTypes(tps: List[Type])(tparam: Symbol): Variance =
+ fold(tps map (tp => varianceInType(tp)(tparam)))
+
+ /** Compute variance of type parameter `tparam` in type `tp`. */
+ def varianceInType(tp: Type)(tparam: Symbol): Variance = {
+ def inArgs(sym: Symbol, args: List[Type]): Variance = fold(map2(args, sym.typeParams)((a, p) => inType(a) * p.variance))
+ def inSyms(syms: List[Symbol]): Variance = fold(syms map inSym)
+ def inTypes(tps: List[Type]): Variance = fold(tps map inType)
+
+ def inSym(sym: Symbol): Variance = if (sym.isAliasType) inType(sym.info).cut else inType(sym.info)
+ def inType(tp: Type): Variance = tp match {
+ case ErrorType | WildcardType | NoType | NoPrefix => Bivariant
+ case ThisType(_) | ConstantType(_) => Bivariant
+ case TypeRef(_, `tparam`, _) => Covariant
+ case BoundedWildcardType(bounds) => inType(bounds)
+ case NullaryMethodType(restpe) => inType(restpe)
+ case SingleType(pre, sym) => inType(pre)
+ case TypeRef(pre, _, _) if tp.isHigherKinded => inType(pre) // a type constructor cannot occur in tp's args
+ case TypeRef(pre, sym, args) => inType(pre) & inArgs(sym, args)
+ case TypeBounds(lo, hi) => inType(lo).flip & inType(hi)
+ case RefinedType(parents, defs) => inTypes(parents) & inSyms(defs.toList)
+ case MethodType(params, restpe) => inSyms(params).flip & inType(restpe)
+ case PolyType(tparams, restpe) => inSyms(tparams).flip & inType(restpe)
+ case ExistentialType(tparams, restpe) => inSyms(tparams) & inType(restpe)
+ case AnnotatedType(annots, tp, _) => inTypes(annots map (_.atp)) & inType(tp)
+ }
+
+ inType(tp)
+ }
+}