diff options
author | Paul Phillips <paulp@improving.org> | 2013-01-01 20:24:26 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-01-09 12:11:14 -0800 |
commit | fb98b7041cc7b4ca63f5888c1131e72a5dcc8411 (patch) | |
tree | 339f846da26fb587ea7fa6740bcecfb09bf70480 | |
parent | 85571f6f5adf6b2488193a6ef4872b2375fec45b (diff) | |
download | scala-fb98b7041cc7b4ca63f5888c1131e72a5dcc8411.tar.gz scala-fb98b7041cc7b4ca63f5888c1131e72a5dcc8411.tar.bz2 scala-fb98b7041cc7b4ca63f5888c1131e72a5dcc8411.zip |
Eliminated redundant validateVariance.
It had repeated all the TypeMap logic. I made it a TypeMap.
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 38 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Variances.scala | 104 |
2 files changed, 61 insertions, 81 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index d2a539db5e..020b8bb4cc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4032,41 +4032,39 @@ trait Types extends api.Types { self: SymbolTable => // throw new Error("mapOver inapplicable for " + tp); } - private def flip() = if (isTrackingVariance) variance = variance.flip - @inline final def flipped[T](body: => T): T = { - flip() - try body finally flip() - } - @inline final def varyOn(tparam: Symbol)(body: => Type): Type = { + def withVariance[T](v: Variance)(body: => T): T = { val saved = variance - variance *= tparam.variance + variance = v try body finally variance = saved } + @inline final def flipped[T](body: => T): T = { + if (isTrackingVariance) variance = variance.flip + try body + finally if (isTrackingVariance) variance = variance.flip + } protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = ( if (isTrackingVariance) - map2Conserve(args, tparams)((arg, tparam) => varyOn(tparam)(this(arg))) + map2Conserve(args, tparams)((arg, tparam) => withVariance(variance * tparam.variance)(this(arg))) else args mapConserve this ) - private def isInfoUnchanged(sym: Symbol) = { - val forceInvariance = isTrackingVariance && !variance.isInvariant && sym.isAliasType - val result = if (forceInvariance) { - val saved = variance - variance = Invariant - try this(sym.info) finally variance = saved - } - else this(sym.info) - - (sym.info eq result) + /** Applies this map to the symbol's info, setting variance = Invariant + * if necessary when the symbol is an alias. + */ + private def applyToSymbolInfo(sym: Symbol): Type = { + if (isTrackingVariance && !variance.isInvariant && sym.isAliasType) + withVariance(Invariant)(this(sym.info)) + else + this(sym.info) } /** Called by mapOver to determine whether the original symbols can - * be returned, or whether they must be cloned. Overridden in VariantTypeMap. + * be returned, or whether they must be cloned. */ protected def noChangeToSymbols(origSyms: List[Symbol]): Boolean = { @tailrec def loop(syms: List[Symbol]): Boolean = syms match { case Nil => true - case x :: xs => isInfoUnchanged(x) && loop(xs) + case x :: xs => (x.info eq applyToSymbolInfo(x)) && loop(xs) } loop(origSyms) } diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index 0db0195a92..1628b4015b 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -23,6 +23,11 @@ trait Variances { // A flag for when we're in a refinement, meaning method parameter types // need to be checked. private var inRefinement = false + @inline private def withinRefinement(body: => Type): Type = { + val saved = inRefinement + inRefinement = true + try body finally inRefinement = saved + } /** Is every symbol in the owner chain between `site` and the owner of `sym` * either a term symbol or private[this]? If not, add `sym` to the set of @@ -54,8 +59,9 @@ trait Variances { && !escapedLocals(sym) ) - /** Validate variance of info of symbol `base` */ - private def validateVariance(base: Symbol) { + private object ValidateVarianceMap extends TypeMap(isTrackingVariance = true) { + private var base: Symbol = _ + /** 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. @@ -79,74 +85,50 @@ trait Variances { ) loop(base, Covariant) } - def isUncheckedVariance(tp: Type) = tp match { case AnnotatedType(annots, _, _) => annots exists (_ matches definitions.uncheckedVarianceClass) case _ => false } - /** 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 = if (!isUncheckedVariance(tp)) tp.withoutAnnotations match { - case ErrorType => - case WildcardType => - case NoType => - case NoPrefix => - case ThisType(_) => - case ConstantType(_) => - case BoundedWildcardType(bounds) => - validateVariance(bounds, variance) - case SingleType(pre, sym) => - validateVariance(pre, variance) - case TypeRef(_, sym, _) if sym.isAliasType => validateVariance(tp.normalize, variance) - - case TypeRef(pre, sym, args) => - if (!sym.variance.isInvariant) { - val relative = relativeVariance(sym) - val required = relative * variance - if (!relative.isBivariant) { - log(s"verifying $sym (${sym.variance}${sym.locationString}) is $required at $base in ${base.owner}") - if (sym.variance != required) - issueVarianceError(base, sym, required) - } - } - validateVariance(pre, variance) - 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 - try decls foreach (sym => validateVariance(sym.info, if (sym.isAliasType) Invariant else variance)) - finally 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) + private def checkVarianceOfSymbol(sym: Symbol) { + val relative = relativeVariance(sym) + val required = relative * variance + if (!relative.isBivariant) { + log(s"verifying $sym (${sym.variance}${sym.locationString}) is $required at $base in ${base.owner}") + if (sym.variance != required) + issueVarianceError(base, sym, required) + } } - - def validateVariances(tps: List[Type], variance: Variance) { - tps foreach (tp => validateVariance(tp, variance)) + override def mapOver(decls: Scope): Scope = { + decls foreach (sym => withVariance(if (sym.isAliasType) Invariant else variance)(this(sym.info))) + decls } - - def validateVarianceArgs(tps: List[Type], variance: Variance, tparams: List[Symbol]) { - foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance)) + /** For PolyTypes, type parameters are skipped because they are defined + * explicitly (their TypeDefs will be passed here.) For MethodTypes, the + * same is true of the parameters (ValDefs) unless we are inside a + * refinement, in which case they are checked from here. + */ + def apply(tp: Type): Type = tp match { + case _ if isUncheckedVariance(tp) => tp + case RefinedType(_, _) => withinRefinement(mapOver(tp)) + case ClassInfoType(parents, _, _) => parents foreach this ; tp + case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize) + case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp) + case mt @ MethodType(_, result) => if (inRefinement) flipped(mt.paramTypes foreach this) ; this(result) + case PolyType(_, result) => this(result) + case _ => mapOver(tp) + } + def validateDefinition(base: Symbol) { + val saved = this.base + this.base = base + try apply(base.info) + finally this.base = saved } + } - validateVariance(base.info, Covariant) + /** Validate variance of info of symbol `base` */ + private def validateVariance(base: Symbol) { + ValidateVarianceMap validateDefinition base } override def traverse(tree: Tree) { |