diff options
author | Paul Phillips <paulp@improving.org> | 2012-12-31 12:11:23 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-01-09 12:11:12 -0800 |
commit | 36ec5ff749a8148637129aa83d2cc597a773272b (patch) | |
tree | 8ce439739f10867985cb134dc471b6bba332b4ad /src/compiler/scala/tools/nsc/typechecker/Variances.scala | |
parent | ea93654a6e9674efa9ab98328462d3b5f59ce91f (diff) | |
download | scala-36ec5ff749a8148637129aa83d2cc597a773272b.tar.gz scala-36ec5ff749a8148637129aa83d2cc597a773272b.tar.bz2 scala-36ec5ff749a8148637129aa83d2cc597a773272b.zip |
Relocated redundant variance checking code.
The variance code in Refchecks is 75+% redundant with
our eventually canonical variance traversing code now in
Variances, but as usual it is time consuming to stamp out
this damaging redundancy so for the moment I am simply
moving it into close proximity with def variancesInType
to abet future work.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Variances.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Variances.scala | 153 |
1 files changed, 151 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala index 44c0ca25b0..66eb415b10 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Variances.scala @@ -6,14 +6,163 @@ package scala.tools.nsc package typechecker -// import scala.reflect.internal.Variance +import scala.reflect.internal.Variance, scala.reflect.internal.Variance._ +import scala.collection.{ mutable, immutable } /** See comments at scala.reflect.internal.Variance. */ trait Variances { val global: Global import global._ - import scala.reflect.internal.Variance._ + import definitions.uncheckedVarianceClass + + class VarianceValidator extends Traverser { + val escapedPrivateLocals = mutable.HashSet[Symbol]() + + /** 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) + + if (!v.isBivariant && sym.variance != v * variance) { + //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG + def tpString(tp: Type) = tp match { + case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner) + case _ => "type "+tp + } + currentRun.currentUnit.error(base.pos, + sym.variance + " " + sym + + " occurs in " + (v * variance) + + " position in " + tpString(base.info) + " of " + base); + } + } + 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 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 = |