Permalink
Browse files

Incorporated Variance value class in Variances.

No one will ever know what it took for me to refine
Variances into its current condition. A LONELY QUEST.
  • Loading branch information...
1 parent 996ee33 commit ea93654a6e9674efa9ab98328462d3b5f59ce91f @paulp paulp committed Dec 31, 2012
@@ -9,6 +9,9 @@ package object nsc {
type Phase = scala.reflect.internal.Phase
val NoPhase = scala.reflect.internal.NoPhase
+ type Variance = scala.reflect.internal.Variance
+ val Variance = scala.reflect.internal.Variance
+
type FatalError = scala.reflect.internal.FatalError
val FatalError = scala.reflect.internal.FatalError
@@ -200,11 +200,12 @@ trait Checkable {
def isNeverSubClass(sym1: Symbol, sym2: Symbol) = areIrreconcilableAsParents(sym1, sym2)
private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/ {
- def isNeverSubArg(t1: Type, t2: Type, variance: Int) = {
- if (variance > 0) isNeverSubType(t2, t1)
- else if (variance < 0) isNeverSubType(t1, t2)
- else isNeverSameType(t1, t2)
- }
+ def isNeverSubArg(t1: Type, t2: Type, variance: Variance) = (
+ if (variance.isInvariant) isNeverSameType(t1, t2)
+ else if (variance.isCovariant) isNeverSubType(t2, t1)
+ else if (variance.isContravariant) isNeverSubType(t1, t2)
+ else false
+ )
exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg)
}
private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
@@ -138,7 +138,7 @@ trait Contexts { self: Analyzer =>
}
var enclMethod: Context = _ // The next outer context whose tree is a method
- var variance: Int = _ // Variance relative to enclosing class
+ var variance: Variance = Variance.Invariant // Variance relative to enclosing class
private var _undetparams: List[Symbol] = List() // Undetermined type parameters,
// not inherited to child contexts
var depth: Int = 0
@@ -198,7 +198,7 @@ trait Infer extends Checkable {
* @throws NoInstance
*/
def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol],
- variances: List[Int], upper: Boolean, depth: Int): List[Type] = {
+ variances: List[Variance], upper: Boolean, depth: Int): List[Type] = {
if (tvars.nonEmpty)
printInference("[solve types] solving for " + tparams.map(_.name).mkString(", ") + " in " + tvars.mkString(", "))
@@ -488,7 +488,7 @@ trait Infer extends Checkable {
pt: Type): List[Type] = {
/** Map type variable to its instance, or, if `variance` is covariant/contravariant,
* to its upper/lower bound */
- def instantiateToBound(tvar: TypeVar, variance: Int): Type = try {
+ def instantiateToBound(tvar: TypeVar, variance: Variance): Type = {
lazy val hiBounds = tvar.constr.hiBounds
lazy val loBounds = tvar.constr.loBounds
lazy val upper = glb(hiBounds)
@@ -501,23 +501,21 @@ trait Infer extends Checkable {
//Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG
if (tvar.constr.inst != NoType)
instantiate(tvar.constr.inst)
- else if ((variance & COVARIANT) != 0 && hiBounds.nonEmpty)
- setInst(upper)
- else if ((variance & CONTRAVARIANT) != 0 && loBounds.nonEmpty)
+ else if (loBounds.nonEmpty && variance.isContravariant)
setInst(lower)
- else if (hiBounds.nonEmpty && loBounds.nonEmpty && upper <:< lower)
+ else if (hiBounds.nonEmpty && (variance.isPositive || loBounds.nonEmpty && upper <:< lower))
setInst(upper)
else
WildcardType
- } catch {
- case ex: NoInstance => WildcardType
}
val tvars = tparams map freshVar
if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
map2(tparams, tvars)((tparam, tvar) =>
- instantiateToBound(tvar, varianceInTypes(formals)(tparam)))
+ try instantiateToBound(tvar, varianceInTypes(formals)(tparam))
+ catch { case ex: NoInstance => WildcardType }
+ )
else
- tvars map (tvar => WildcardType)
+ tvars map (_ => WildcardType)
}
/** [Martin] Can someone comment this please? I have no idea what it's for
@@ -571,8 +569,8 @@ trait Infer extends Checkable {
foreach3(tparams, tvars, targs) { (tparam, tvar, targ) =>
val retract = (
- targ.typeSymbol == NothingClass // only retract Nothings
- && (restpe.isWildcard || (varianceInType(restpe)(tparam) & COVARIANT) == 0) // don't retract covariant occurrences
+ targ.typeSymbol == NothingClass // only retract Nothings
+ && (restpe.isWildcard || !varianceInType(restpe)(tparam).isPositive) // don't retract covariant occurrences
)
buf += ((tparam,
@@ -1316,7 +1314,7 @@ trait Infer extends Checkable {
val tvars1 = tvars map (_.cloneInternal)
// Note: right now it's not clear that solving is complete, or how it can be made complete!
// So we should come back to this and investigate.
- solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (x => COVARIANT), false)
+ solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (_ => Variance.Covariant), false)
}
// this is quite nasty: it destructively changes the info of the syms of e.g., method type params (see #3692, where the type param T's bounds were set to >: T <: T, so that parts looped)
@@ -11,6 +11,7 @@ import scala.collection.{ mutable, immutable }
import transform.InfoTransform
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
+import scala.reflect.internal.Variance._
/** <p>
* Post-attribution checking and transformation.
@@ -844,10 +845,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// Variance Checking --------------------------------------------------------
- private val NoVariance = 0
- private val CoVariance = 1
- private val AnyVariance = 2
-
private val escapedPrivateLocals = new mutable.HashSet[Symbol]
val varianceValidator = new Traverser {
@@ -858,41 +855,36 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// need to be checked.
var inRefinement = false
- def varianceString(variance: Int): String =
- if (variance == 1) "covariant"
- else if (variance == -1) "contravariant"
- else "invariant";
-
/** 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): Int = {
+ def relativeVariance(tvar: Symbol): Variance = {
val clazz = tvar.owner
var sym = base
- var state = CoVariance
- while (sym != clazz && state != AnyVariance) {
+ var state: Variance = Covariant
+ while (sym != clazz && state != Bivariant) {
//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;
+ 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 AnyVariance if `sym` is local to a term
// or is private[this] or protected[this]
- state = AnyVariance
+ state = Bivariant
} else if (sym.isAliasType) {
- // return AnyVariance if `sym` is an alias type
+ // 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 NoVariance, as there might then be
+ // we have to assume Invariant, as there might then be
// references to the type parameter that are not variance checked.
- state = if (sym.isOverridingSymbol) NoVariance else AnyVariance
+ state = if (sym.isOverridingSymbol) Invariant else Bivariant
}
sym = sym.owner
}
@@ -902,7 +894,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
/** Validate that the type `tp` is variance-correct, assuming
* the type occurs itself at variance position given by `variance`
*/
- def validateVariance(tp: Type, variance: Int): Unit = tp match {
+ def validateVariance(tp: Type, variance: Variance): Unit = tp match {
case ErrorType =>
case WildcardType =>
case BoundedWildcardType(bounds) =>
@@ -915,19 +907,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
validateVariance(pre, variance)
case TypeRef(pre, sym, args) =>
// println("validate "+sym+" at "+relativeVariance(sym))
- if (sym.isAliasType/* && relativeVariance(sym) == AnyVariance*/)
+ if (sym.isAliasType/* && relativeVariance(sym) == Bivariant*/)
validateVariance(tp.normalize, variance)
- else if (sym.variance != NoVariance) {
+ else if (!sym.variance.isInvariant) {
val v = relativeVariance(sym)
- if (v != AnyVariance && sym.variance != v * variance) {
+ 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
}
unit.error(base.pos,
- varianceString(sym.variance) + " " + sym +
- " occurs in " + varianceString(v * variance) +
+ sym.variance + " " + sym +
+ " occurs in " + (v * variance) +
" position in " + tpString(base.info) + " of " + base);
}
}
@@ -944,14 +936,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
val saved = inRefinement
inRefinement = true
for (sym <- decls)
- validateVariance(sym.info, if (sym.isAliasType) NoVariance else variance)
+ validateVariance(sym.info, if (sym.isAliasType) Invariant else variance)
inRefinement = saved
case TypeBounds(lo, hi) =>
- validateVariance(lo, -variance)
+ validateVariance(lo, variance.flip)
validateVariance(hi, variance)
case mt @ MethodType(formals, result) =>
if (inRefinement)
- validateVariances(mt.paramTypes, -variance)
+ validateVariances(mt.paramTypes, variance.flip)
validateVariance(result, variance)
case NullaryMethodType(result) =>
validateVariance(result, variance)
@@ -966,15 +958,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
validateVariance(tp, variance)
}
- def validateVariances(tps: List[Type], variance: Int) {
+ def validateVariances(tps: List[Type], variance: Variance) {
tps foreach (tp => validateVariance(tp, variance))
}
- def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) {
+ def validateVarianceArgs(tps: List[Type], variance: Variance, tparams: List[Symbol]) {
foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance))
}
- validateVariance(base.info, CoVariance)
+ validateVariance(base.info, Covariant)
}
override def traverse(tree: Tree) {
@@ -169,11 +169,6 @@ trait TypeDiagnostics {
case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ")
}
- def varianceWord(sym: Symbol): String =
- if (sym.variance == 1) "covariant"
- else if (sym.variance == -1) "contravariant"
- else "invariant"
-
def explainAlias(tp: Type) = {
// Don't automatically normalize standard aliases; they still will be
// expanded if necessary to disambiguate simple identifiers.
@@ -223,7 +218,7 @@ trait TypeDiagnostics {
)
val explainDef = {
val prepend = if (isJava) "Java-defined " else ""
- "%s%s is %s in %s.".format(prepend, reqsym, varianceWord(param), param)
+ "%s%s is %s in %s.".format(prepend, reqsym, param.variance, param)
}
// Don't suggest they change the class declaration if it's somewhere
// under scala.* or defined in a java class, because attempting either
@@ -243,7 +238,7 @@ trait TypeDiagnostics {
|| ((arg <:< reqArg) && param.isCovariant)
|| ((reqArg <:< arg) && param.isContravariant)
)
- val invariant = param.variance == 0
+ val invariant = param.variance.isInvariant
if (conforms) Some("")
else if ((arg <:< reqArg) && invariant) mkMsg(true) // covariant relationship
@@ -938,7 +938,7 @@ trait Typers extends Modes with Adaptations with Tags {
val unapply = unapplyMember(extractor.tpe)
val clazz = unapplyParameterType(unapply)
- if (unapply.isCase && clazz.isCase && !(clazz.ancestors exists (_.isCase))) {
+ if (unapply.isCase && clazz.isCase) {
// convert synthetic unapply of case class to case class constructor
val prefix = tree.tpe.prefix
val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner))
@@ -947,8 +947,9 @@ trait Typers extends Modes with Adaptations with Tags {
val skolems = new mutable.ListBuffer[TypeSymbol]
object variantToSkolem extends VariantTypeMap {
def apply(tp: Type) = mapOver(tp) match {
- case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm =>
- val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe)
+ // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189
+ case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm =>
+ val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe)
// origin must be the type param so we can deskolemize
val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds)
// println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree )
Oops, something went wrong.

0 comments on commit ea93654

Please sign in to comment.