diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 152 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Variances.scala | 153 |
2 files changed, 154 insertions, 151 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 71e04db24c..eeb76dced2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -11,7 +11,6 @@ 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. @@ -39,7 +38,7 @@ import scala.reflect.internal.Variance._ * * @todo Check whether we always check type parameter bounds. */ -abstract class RefChecks extends InfoTransform with scala.reflect.internal.transform.RefChecks { +abstract class RefChecks extends InfoTransform with scala.reflect.internal.transform.RefChecks with Variances { val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue @@ -845,152 +844,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Variance Checking -------------------------------------------------------- - private val escapedPrivateLocals = new mutable.HashSet[Symbol] - - val varianceValidator = new Traverser { - - /** 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 != 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.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 = 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 - } - unit.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 _ => - } - } - } + val varianceValidator = new VarianceValidator // Forward reference checking --------------------------------------------------- @@ -1596,7 +1450,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans hidden = o.isTerm || o.isPrivateLocal o = o.owner } - if (!hidden) escapedPrivateLocals += sym + if (!hidden) varianceValidator.escapedPrivateLocals += sym } def checkSuper(mix: Name) = 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 = |