Skip to content

Commit

Permalink
Eliminated redundant validateVariance.
Browse files Browse the repository at this point in the history
It had repeated all the TypeMap logic. I made it a TypeMap.
  • Loading branch information
paulp committed Jan 9, 2013
1 parent 85571f6 commit fb98b70
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 81 deletions.
38 changes: 18 additions & 20 deletions src/reflect/scala/reflect/internal/Types.scala
Expand Up @@ -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)
}
Expand Down
104 changes: 43 additions & 61 deletions src/reflect/scala/reflect/internal/Variances.scala
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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) {
Expand Down

0 comments on commit fb98b70

Please sign in to comment.