diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/RefChecks.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala new file mode 100644 index 0000000000..f60ffae9b5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -0,0 +1,589 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; +import collection.mutable.HashMap; +import transform.InfoTransform; + +/** Post-attribution checking and transformation. + * //todo: check whether we always check type parameter bounds. + * + * This phase performs the following checks. + * + * - All overrides conform to rules. + * - All type arguments conform to bounds. + * - All type variable uses conform to variance annotations. + * - No forward reference to a term symbol extends beyond a value definition. + * + * It performs the following transformations. + * + * - Local modules are replaced by variables and classes + * - Calls to case factory methods are replaced by new's. + * - References to parameter accessors with aliases are replaced by super references to + * these aliases. + */ +abstract class RefChecks extends InfoTransform { + + import global._; + import definitions._; + import typer.{typed, typedOperator, atOwner}; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "refchecks"; + override def phaseNewFlags: long = lateMETHOD; + + def newTransformer(unit: CompilationUnit): RefCheckTransformer = new RefCheckTransformer(unit); + override def changesBaseClasses = false; + + def transformInfo(sym: Symbol, tp: Type): Type = { + if (sym.isModule && !sym.isStatic) { + sym setFlag (lateMETHOD | STABLE); + PolyType(List(), tp) + } else tp + } + + // var m$: T = null; or, if class member: local var m$: T = _; + def newModuleVarDef(accessor: Symbol) = { + val mvar = accessor.owner.newVariable(accessor.pos, nme.moduleVarName(accessor.name)) + .setInfo(accessor.tpe.finalResultType); + if (mvar.owner.isClass) { + mvar setFlag (PRIVATE | LOCAL | SYNTHETIC); + mvar.owner.info.decls.enter(mvar); + } + ValDef(mvar, if (mvar.owner.isClass) EmptyTree else Literal(Constant(null))) + } + + // def m: T = { if (m$ == null) m$ = new m$class; m$ } + def newModuleAccessDef(accessor: Symbol, mvar: Symbol) = { + var mvarRef = if (mvar.owner.isClass) Select(This(mvar.owner), mvar) else Ident(mvar); + DefDef(accessor, vparamss => + Block( + List( + If( + Apply(Select(mvarRef, nme.eq), List(Literal(Constant(null)))), + Assign(mvarRef, + New(TypeTree(mvar.tpe), + List(for (val pt <- mvar.tpe.symbol.primaryConstructor.info.paramTypes) + yield This(accessor.owner.enclClass)))),//??? + EmptyTree)), + mvarRef)) + } + + // def m: T; + def newModuleAccessDcl(accessor: Symbol) = + DefDef(accessor setFlag lateDEFERRED, vparamss => EmptyTree); + + class RefCheckTransformer(unit: CompilationUnit) extends Transformer { + + var localTyper: analyzer.Typer = typer; + +// Override checking ------------------------------------------------------------ + + /** 1. Check all members of class `clazz' for overriding conditions. + * That is for overriding member M and overridden member O: + * + * 1.1. M must have the same or stronger access privileges as O. + * 1.2. O must not be final. + * 1.3. O is deferred, or M has `override' modifier. + * 1.4. If O is an immutable value, then so is M. + * 1.5. Neither M nor O are a parameterized type alias + * 1.6. If O is a type alias, then M is an alias of O. + * 1.7. If O is an abstract type then + * either M is an abstract type, and M's bounds are sharper than O's bounds. + * or M is an unparameterized type alias or class which conforms to O's bounds. + * 1.8. If O and M are values, then M's type is a subtype of O's type. + * 2. Check that only abstract classes have deferred members + * 3. Check that every member with an `override' modifier + * overrides some other member. + */ + private def checkAllOverrides(clazz: Symbol): unit = { + + val self = clazz.thisType; + + def infoString(sym: Symbol) = ( + sym.toString() + + (if (sym.owner == clazz) "" + else (sym.locationString + + (if (sym.isAliasType) ", which equals " + self.memberInfo(sym) + else if (sym.isAbstractType) " with bounds " + self.memberInfo(sym) + else if (sym.isTerm) " of type " + self.memberInfo(sym) + else ""))) + ); + + /* Check that all conditions for overriding `other' by `member' are met. */ + def checkOverride(clazz: Symbol, member: Symbol, other: Symbol): unit = { + val pos = if (member.owner == clazz) member.pos else clazz.pos; + + def overrideError(msg: String): unit = + if (other.tpe != ErrorType && member.tpe != ErrorType) + unit.error(pos, "error overriding " + infoString(other) + + ";\n " + infoString(member) + " " + msg); + + def overrideTypeError(): unit = { + if (other.tpe != ErrorType && member.tpe != ErrorType) { + overrideError("has incompatible type"); + explainTypes(member.tpe, other.tpe); + } + } + + //System.out.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG + + // return if we already checked this combination elsewhere + if (member.owner != clazz) { + if ((member.owner isSubClass other.owner) && + ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED))) { + //System.out.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG + return; + } + if (clazz.info.parents exists (parent => + (parent.symbol isSubClass other.owner) && (parent.symbol isSubClass member.owner) && + ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED)))) { + //System.out.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG + return; + } + if (clazz.info.parents forall (parent => + (parent.symbol isSubClass other.owner) == (parent.symbol isSubClass member.owner))) { + //System.out.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG + return; + } + } + + if (member hasFlag PRIVATE) { // (1.1) + overrideError("has weaker access privileges; it should not be private"); + } else if ((member hasFlag PROTECTED) && !(other hasFlag PROTECTED)) { // 1 + overrideError("has weaker access privileges; it should not be protected"); + } else if (other hasFlag FINAL) { // (1.2) + overrideError("cannot override final member"); + } else if (!(other hasFlag DEFERRED) && !(member hasFlag (OVERRIDE | ABSOVERRIDE))) { // (1.3) + overrideError("needs `override' modifier"); + } else if ((other hasFlag ABSOVERRIDE) && other.isIncompleteIn(clazz) && !(member hasFlag ABSOVERRIDE)) { + overrideError("needs `abstract override' modifiers"); + } else if (other.isStable && !member.isStable) { // (1.4) + overrideError("needs to be an immutable value"); + } else { + if (other.isAliasType) { + if (!member.typeParams.isEmpty) // (1.5) + overrideError("may not be parameterized"); + if (!other.typeParams.isEmpty) // (1.5) + overrideError("may not override parameterized type"); + if (!(self.memberType(member) =:= self.memberType(other))) // (1.6) + overrideTypeError(); + } else if (other.isAbstractType) { + if (!member.typeParams.isEmpty) // (1.7) + overrideError("may not be parameterized"); + if (!(self.memberInfo(other).bounds containsType self.memberType(member))) // (1.7) + overrideTypeError(); + } else if (other.isTerm) { + if (!(self.memberInfo(member) <:< (self.memberInfo(other)))) // 8 + overrideTypeError(); + } + } + } + + val opc = new overridingPairs.Cursor(clazz); + while (opc.hasNext) { + //System.out.println("overrides " + opc.overriding/* + ":" + opc.overriding.tpe*/ + opc.overriding.locationString + " " + opc.overridden/* + ":" + opc.overridden.tpe*/ + opc.overridden.locationString + opc.overridden.hasFlag(DEFERRED));//DEBUG + if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden); + + opc.next + } +/* + // 1. Check all members for overriding conditions. + for (val bc <- clazz.info.baseClasses.tail; val other <- bc.info.decls.toList) + if (!other.isClass && !(other hasFlag PRIVATE) && !other.isConstructor) { + val member = clazz.tpe.member(other.name) filter + (sym => sym.owner != other.owner && + (sym.isType || (self.memberType(sym) matches self.memberType(other)))); + if (member hasFlag OVERLOADED) { + val alt1 = member.alternatives.head; + val alt2 = member.alternatives.tail.head; + val pos = if (alt1.owner == clazz) alt1.pos + else if (alt2.owner == clazz) alt2.pos + else clazz.pos; + unit.error(pos, + "ambiguous override: both " + infoString(alt1) + + "\n and " + infoString(alt2) + + "\n override " + infoString(other)); + } else if (member != NoSymbol && !(member hasFlag LOCAL)) { + System.out.println("OVERRIDES " + member + member.locationString + " " + other + other.locationString);//debug + checkOverride(clazz, member, other); + } + } +*/ + // 2. Check that only abstract classes have deferred members + if (clazz.isClass && !clazz.isTrait) { + def abstractClassError(mustBeTrait: boolean, msg: String): unit = { + unit.error(clazz.pos, + (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible" + else if (mustBeTrait) clazz.toString() + " needs to be a trait" + else clazz.toString() + " needs to be abstract") + ", since " + msg); + clazz.setFlag(ABSTRACT); + } + for (val member <- clazz.tpe.members) + if ((member hasFlag DEFERRED) && !(clazz hasFlag ABSTRACT)) { + abstractClassError(false, + infoString(member) + " is not defined" + + (if (member.isVariable || member.hasFlag(ACCESSOR)) + "\n(Note that variables need to be initialized to be defined)" else "")) + } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) { + val other = member.superSymbol(clazz); + abstractClassError(true, + infoString(member) + " is marked `abstract' and `override'" + + (if (other != NoSymbol) + " and overrides incomplete superclass member " + infoString(other) + else "")) + } + } + + // 3. Check that every defined member with an `override' modifier overrides some other member. + for (val member <- clazz.info.decls.toList) + if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) && + (clazz.info.baseClasses.tail forall { + bc => member.matchingSymbol(bc, clazz.thisType) == NoSymbol + })) { + // for (val bc <- clazz.info.baseClasses.tail) System.out.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG + unit.error(member.pos, member.toString() + " overrides nothing"); + member resetFlag OVERRIDE + } + } + + // Basetype Checking -------------------------------------------------------- + + /** 1. Check that later type instances in the base-type sequence + * are subtypes of earlier type instances of the same trait. + * 2. Check that case classes do not inherit from case classes. + * 3. Check that at most one base type is a case-class. + */ + private def validateBaseTypes(clazz: Symbol): unit = { + val seenTypes = new Array[Type](clazz.info.closure.length); + var seenCaseClass = if (clazz hasFlag CASE) clazz else NoSymbol; + + def validateTypes(tps: List[Type], includeSuper: boolean): unit = { + if (!tps.isEmpty) { + for (val tp <- tps.tail.reverse) validateType(tp, false); + if (includeSuper) validateType(tps.head, true); + } + } + + def validateType(tp: Type, includeSuper: boolean): unit = { + val baseClass = tp.symbol; + if (baseClass.isClass) { + val index = clazz.info.closurePos(baseClass); + if (index >= 0) { + if (seenTypes(index) != null && !(seenTypes(index) <:< tp)) + unit.error(clazz.pos, "illegal inheritance;\n " + clazz + + " inherits different type instances of " + baseClass + + ":\n" + tp + " and " + seenTypes(index)); + seenTypes(index) = tp; + // check that case classes do not inherit from case classes + if (baseClass hasFlag CASE) { + if (seenCaseClass != NoSymbol && seenCaseClass != baseClass) + unit.error(clazz.pos, "illegal combination of case " + + seenCaseClass + " and case " + baseClass + " in one object"); + seenCaseClass = baseClass + } + } + validateTypes(tp.parents, includeSuper); + } + } + + validateTypes(clazz.info.parents, true); + } + + // Variance Checking -------------------------------------------------------- + + private val ContraVariance = -1; + private val NoVariance = 0; + private val CoVariance = 1; + private val AnyVariance = 2; + + /** Check variance of type variables in this type + */ + private def validateVariance(base: Symbol, all: Type, variance: int): unit = { + + def varianceString(variance: int): String = + if (variance == 1) "covariant" + else if (variance == -1) "contravariant" + else "invariant"; + + def relativeVariance(tvar: Symbol): int = { + val clazz = tvar.owner; + var sym = base; + var state = CoVariance; + while (sym != clazz && state != AnyVariance) { + //System.out.println("flip: " + sym + " " + sym.isParameter());//DEBUG + if ((sym hasFlag PARAM) && !sym.owner.isConstructor) state = -state; + else if (!sym.owner.isClass) state = AnyVariance; + else if (sym.isAliasType) state = NoVariance; + sym = sym.owner; + } + state + } + + def validateVariance(tp: Type, variance: int): unit = tp match { + case ErrorType => ; + case WildcardType => ; + case NoType => ; + case NoPrefix => ; + case ThisType(_) => ; + case ConstantType(_) => ; + case SingleType(pre, sym) => + validateVariance(pre, variance) + case TypeRef(pre, sym, args) => + if (sym.variance != NoVariance) { + val v = relativeVariance(sym); + if (v != AnyVariance && sym.variance != v * variance) { + //System.out.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG + unit.error(base.pos, + varianceString(sym.variance) + " " + sym + + " occurs in " + varianceString(v * variance) + + " position in type " + all + " of " + base); + } + } + validateVariance(pre, variance); + validateVarianceArgs(args, variance, sym.typeParams); + case ClassInfoType(parents, decls, symbol) => + validateVariances(parents, variance); + case RefinedType(parents, decls) => + validateVariances(parents, variance); + case TypeBounds(lo, hi) => + validateVariance(lo, -variance); + validateVariance(hi, variance); + case MethodType(formals, result) => + validateVariance(result, variance); + case PolyType(tparams, result) => + validateVariance(result, variance); + } + + def validateVariances(tps: List[Type], variance: int): unit = + tps foreach (tp => validateVariance(tp, variance)); + + def validateVarianceArgs(tps: List[Type], variance: int, tparams: List[Symbol]): unit = + (tps zip tparams) foreach { + case Pair(tp, tparam) => validateVariance(tp, variance * tparam.variance) + } + + validateVariance(all, variance) + } + +// Forward reference checking --------------------------------------------------- + + class LevelInfo(val outer: LevelInfo) { + val scope: Scope = if (outer == null) new Scope() else new Scope(outer.scope); + var maxindex: int = Integer.MIN_VALUE; + var refpos: int = _; + var refsym: Symbol = _; + } + + private var currentLevel: LevelInfo = null; + private val symIndex = new HashMap[Symbol, int]; + + private def pushLevel(): unit = + currentLevel = new LevelInfo(currentLevel); + + private def popLevel(): unit = + currentLevel = currentLevel.outer; + + private def enterSyms(stats: List[Tree]): unit = { + var index = -1; + for (val stat <- stats) { + index = index + 1; + stat match { + case ClassDef(_, _, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => + assert(stat.symbol != NoSymbol, stat);//debug + if (stat.symbol.isLocal) { + currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope)); + symIndex(stat.symbol) = index; + } + case _ => + } + } + } + + private def enterReference(pos: int, sym: Symbol): unit = + if (sym.isLocal) { + val e = currentLevel.scope.lookupEntry(sym.name); + if (e != null && sym == e.sym) { + var l = currentLevel; + while (l.scope != e.owner) l = l.outer; + val symindex = symIndex(sym); + if (l.maxindex < symindex) { + l.refpos = pos; + l.refsym = sym; + l.maxindex = symindex; + } + } + } + +// Transformation ------------------------------------------------------------ + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + pushLevel(); + enterSyms(stats); + var index = -1; + val stats1 = stats flatMap { stat => index = index + 1; transformStat(stat, index) } + popLevel(); + stats1 + } + + def transformStat(tree: Tree, index: int): List[Tree] = tree match { + case ModuleDef(mods, name, impl) => + val sym = tree.symbol; + val cdef = ClassDef(mods | MODULE, name, List(), EmptyTree, impl) + .setPos(tree.pos) + .setSymbol(sym.moduleClass) + .setType(NoType); + if (sym.isStatic) List(transform(cdef)) + else { + val vdef = + localTyper.typed { + atPos(tree.pos) { + newModuleVarDef(sym) + } + } + + val ddef = + atPhase(phase.next) { + localTyper.typed { + if (sym.owner.isTrait) newModuleAccessDcl(sym) + else newModuleAccessDef(sym, vdef.symbol) + } + } + + if (sym.owner.isTrait) transformTrees(List(cdef, ddef)) + else transformTrees(List(cdef, vdef, ddef)) + } + + case ValDef(_, _, _, _) => + val tree1 = transform(tree); // important to do before forward reference check + if (tree.symbol.isLocal && index <= currentLevel.maxindex) { + if (settings.debug.value) System.out.println(currentLevel.refsym); + unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol); + } + List(tree1) + + case Import(_, _) => + List() + + case _ => + List(transform(tree)) + } + + override def transform(tree: Tree): Tree = try { + + /* Convert a reference of a case factory to a new of the class it produces. */ + def toConstructor: Tree = { + var tpe = tree.tpe; + while (!tpe.symbol.isClass) tpe = tpe.resultType; + assert(tpe.symbol hasFlag CASE); + typedOperator(atPos(tree.pos)(Select(New(TypeTree(tpe)), tpe.symbol.primaryConstructor))); + } + + /* Check whether argument types conform to bounds of type parameters */ + def checkBounds(tparams: List[Symbol], argtps: List[Type]): unit = try { + typer.infer.checkBounds(tree.pos, tparams, argtps, ""); + } catch { + case ex: TypeError => unit.error(tree.pos, ex.getMessage()); + } + + val savedLocalTyper = localTyper; + val sym = tree.symbol; + var result = tree; + tree match { + case ClassDef(mods, name, tparams, tpe, impl) => + validateVariance(sym, sym.info, CoVariance); + validateVariance(sym, sym.typeOfThis, CoVariance); + + case DefDef(_, _, _, _, _, _) => + validateVariance(sym, sym.tpe, CoVariance); + + case ValDef(_, _, _, _) => + validateVariance(sym, sym.tpe, if (sym.isVariable) NoVariance else CoVariance); + + case AbsTypeDef(_, _, _, _) => + validateVariance(sym, sym.info, CoVariance); + + case AliasTypeDef(_, _, _, _) => + validateVariance(sym, sym.info, CoVariance); + + case Template(_, _) => + localTyper = localTyper.atOwner(tree, currentOwner); + validateBaseTypes(currentOwner); + checkAllOverrides(currentOwner); + + case TypeTree() => + new TypeTraverser { + def traverse(tp: Type) = tp match { + case TypeRef(pre, sym, args) => checkBounds(sym.typeParams, args); this + case _ => this + } + } traverse tree.tpe + + case TypeApply(fn, args) => + checkBounds(fn.tpe.typeParams, args map (.tpe)); + if (sym.isSourceMethod && sym.hasFlag(CASE)) result = toConstructor; + + case New(tpt) => + enterReference(tree.pos, tpt.tpe.symbol); + + case Ident(name) => + if (sym.isSourceMethod && sym.hasFlag(CASE)) + result = toConstructor + else if (name != nme.WILDCARD && name != nme.WILDCARD_STAR.toTypeName) { + assert(sym != NoSymbol, tree);//debug + enterReference(tree.pos, sym); + } + + case Select(qual, name) => + if (sym.isSourceMethod && sym.hasFlag(CASE)) + result = toConstructor + else qual match { + case Super(qualifier, mixin) => + val base = currentOwner.enclClass; + if (sym hasFlag DEFERRED) { + val member = sym.overridingSymbol(base);//??? + if (mixin != nme.EMPTY.toTypeName || member == NoSymbol || + !((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(base))) + unit.error(tree.pos, "symbol accessed from super may not be abstract"); + } + //System.out.println("super: " + tree + " in " + base);//DEBUG + if (base.isTrait && sym.isTerm && mixin == nme.EMPTY.toTypeName) { + val superAccName = nme.superName(sym.name); + val superAcc = base.info.decl(superAccName) suchThat (.alias.==(sym)); + assert(superAcc != NoSymbol, "" + sym + " " + base + " " + superAccName);//debug + val tree1 = Select(This(base), superAcc); + if (settings.debug.value) log("super-replacement: " + tree + "=>" + tree1); + result = atPos(tree.pos) { + Select(gen.This(base), superAcc) setType superAcc.tpe + } + } + case This(_) => + if ((sym hasFlag PARAMACCESSOR) && (sym.alias != NoSymbol)) { + result = typed { + Select( + Super(qual.symbol, qual.symbol.info.parents.head.symbol.name) setPos qual.pos, + sym.alias) setPos tree.pos + } + if (settings.debug.value) + System.out.println("alias replacement: " + tree + " ==> " + result);//debug + } + case _ => + } + case _ => + } + result = super.transform(result); + localTyper = savedLocalTyper; + result + } catch { + case ex: TypeError => + if (settings.debug.value) ex.printStackTrace(); + unit.error(tree.pos, ex.getMessage()); + tree + } + } +} |