/* ____ ____ ____ ____ ______ *\
** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
** /_____/\____/\___/\____/____/ **
\* */
// $Id$
package scalac.typechecker;
import java.util.HashMap;
import java.util.Iterator;
import scalac.*;
import scalac.util.*;
import scalac.ast.*;
import scalac.ast.printer.*;
import scalac.symtab.*;
import Tree.*;
/** Post-attribution checking and transformation.
*
* 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 preforms the following transformations.
*
* - Local modules are replaced by variables and classes
* - toString, equals, and hashCode methods are added to case classes, unless
* they are defined in the class or a baseclass different from java.lang.Object
* - Calls to case factory methods are replaced by new's.
* - Type nodes are replaced by TypeTerm nodes.
*/
public class RefCheck extends Transformer implements Modifiers, Kinds {
public RefCheck(Global global) {
super(global);
}
private Unit unit;
private Definitions defs = global.definitions;
private Infer infer = new Infer(this);
private Symbol enclClass;
public void apply(Unit unit) {
this.unit = unit;
level = 0;
scopes[0] = new Scope();
maxindex[0] = Integer.MIN_VALUE;
unit.body = transformStats(unit.body);
scopes[0] = null;
symIndex.clear();
}
// Override checking ------------------------------------------------------------
static boolean isIncomplete(Symbol sym) {
return sym.isDeferred() ||
sym.isAbstractOverride() &&
isIncomplete(sym.overriddenSymbol(sym.owner().parents()[0]));
}
/** 1. Check all members of class `clazz' for overriding conditions.
* 2. Check that only abstract classes have deferred members*
* 3. Check that every member with an `override' modifier
* overrides a concrete member.
*/
void checkAllOverrides(int pos, Symbol clazz) {
Type[] closure = clazz.closure();
for (int i = 0; i < closure.length; i++) {
for (Scope.SymbolIterator it = closure[i].members().iterator(true);
it.hasNext();) {
checkOverride(pos, clazz, it.next());
}
}
Type[] parents = clazz.info().parents();
for (Scope.SymbolIterator it = clazz.members().iterator(true);
it.hasNext();) {
Symbol sym = it.next();
if ((sym.flags & OVERRIDE) != 0) {
int i = parents.length - 1;
while (i >= 0 && sym.overriddenSymbol(parents[i]).kind == NONE)
i--;
if (i < 0) {
unit.error(sym.pos, sym + " overrides nothing");
sym.flags &= ~OVERRIDE;
} else if (sym.isAbstractOverride() &&
sym.overriddenSymbol(parents[0]).kind == NONE) {
unit.error(sym.pos,
sym + " does not override a superclass member in " +
parents);
}
}
}
}
void checkOverride(int pos, Symbol clazz, Symbol other) {
Symbol member = other;
if ((other.flags & PRIVATE) == 0) {
Symbol member1 = clazz.info().lookup(other.name);
if (member1.kind != NONE && member1.owner() != other.owner()) {
if (member1.kind == VAL) {
Type self = clazz.thisType();
Type otherinfo = normalizedInfo(self, other);
Type template = resultToAny(otherinfo);
switch (member1.info()) {
case OverloadedType(Symbol[] alts, _):
for (int i = 0; i < alts.length; i++) {
if (normalizedInfo(self, alts[i]).isSubType(template)) {
if (member == other)
member = alts[i];
else
unit.error(
pos,
"ambiguous override: both " +
member + ":" + normalizedInfo(self, member) +
"\n and " + alts[i] + ":" + normalizedInfo(self, alts[i]) +
"\n override " + other + ":" + otherinfo +
other.locationString());
}
}
break;
default:
if (normalizedInfo(self, member1).isSubType(template)) {
member = member1;
}
}
} else {
member = member1;
}
}
if (member != other) {
checkOverride(pos, clazz, member, other);
}
}
if (clazz.kind == CLASS && (clazz.flags & ABSTRACT) == 0) {
if ((member.flags & DEFERRED) != 0) {
abstractClassError(
clazz,
member + member.locationString() + " is not defined" +
(((member.flags & MUTABLE) == 0) ? ""
: "\n(Note that variables need to be initialized to be defined)"));
} else if (member.isAbstractOverride()) {
Type superclazz = clazz.parents()[0];
Symbol sup = member.overriddenSymbol(superclazz);
if (clazz.kind == CLASS && (clazz.flags & ABSTRACT) == 0 &&
isIncomplete(sup)) {
abstractClassError(
clazz, member + member.locationString() +
" is marked `abstract' and `override' and overrides an incomplete superclass member in " + superclazz);
}
}
}
}
//where
private Type resultToAny(Type tp) {
switch (tp) {
case PolyType(Symbol[] tparams, Type restp):
return Type.PolyType(tparams, resultToAny(restp));
case MethodType(Symbol[] tparams, Type restp):
return Type.MethodType(tparams, Type.AnyType);
default:
return defs.ANY_TYPE;
}
}
private void abstractClassError(Symbol clazz, String msg) {
if (clazz.isAnonymousClass())
unit.error(clazz.pos, "object creation impossible, since " + msg);
else
unit.error(clazz.pos, clazz + " needs to be abstract, since " + msg);
clazz.flags |= ABSTRACT;
}
/** Check that all conditions for overriding `other' by `member' are met.
* That is for overriding member M and overridden member O:
*
* 1. M must have the same or stronger access privileges as O.
* 2. O must not be final.
* 3. O is deferred, or M has `override' modifier.
* 4. O is not a class, nor a class constructor.
* 5. If O is a type alias, then M is an alias of O.
* 6. 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 a type alias or class which conforms to O's bounds.
* 7. If O and M are values, then M's type is a subtype of O's type.
* 8. If O is an immutable value, then so is M.
* 9. If O is a member of the static superclass of the class in which
* M is defined, and O is labelled `abstract override', then
* M must be labelled `abstract override'.
*/
void checkOverride(int pos, Symbol clazz, Symbol member, Symbol other) {
if (member.owner() == clazz)
pos = member.pos;
else if (member.owner().isSubClass(other.owner()))
return; // everything was already checked elsewhere
if ((member.flags & PRIVATE) != 0) {
overrideError(pos, member, other, "has weaker access privileges; it should not be private");
} else if ((member.flags & PROTECTED) != 0 && (other.flags & PROTECTED) == 0) {
overrideError(pos, member, other, "has weaker access privileges; it should not be protected");
} else if ((other.flags & FINAL) != 0) {
overrideError(pos, member, other, "cannot override final member");
} else if (other.kind == CLASS) {
overrideError(pos, member, other, "cannot override a class");
} else if ((other.flags & DEFERRED) == 0 && ((member.flags & OVERRIDE) == 0)) {
overrideError(pos, member, other, "needs `override' modifier");
} else if (other.isAbstractOverride() &&
!member.isAbstractOverride() &&
member.owner() == clazz &&
clazz.parents()[0].symbol().isSubClass(other.owner())) {
overrideError(pos, member, other, "needs `abstract' and `override' modifiers");
} else if (other.isStable() && !member.isStable()) {
overrideError(pos, member, other, "needs to be an immutable value");
} else if ((member.flags & DEFERRED) == 0 && (other.flags & DEFERRED) == 0 &&
member.owner() != clazz &&
!clazz.parents()[0].symbol().isSubClass(other.owner())) {
unit.error(pos, "conflict between concrete members " +
member + member.locationString() + " and " +
other + other.locationString() +
":\n both are inherited from mixin classes; " +
"\n an overriding definition in the current template is required");
} else {
Type self = clazz.thisType();
switch (other.kind) {
case ALIAS:
if (member.typeParams().length != 0)
overrideError(pos, member, other, "may not be parameterized");
if (other.typeParams().length != 0)
overrideError(pos, member, other, "may not override parameterized type");
if (!self.memberType(member).isSameAs(self.memberType(other)))
overrideTypeError(pos, member, other, self, false);
break;
case TYPE:
if (member.typeParams().length != 0)
overrideError(pos, member, other, "may not be parameterized");
if (!self.memberInfo(member).isSubType(self.memberInfo(other)))
overrideTypeError(pos, member, other, self, false);
if (!self.memberLoBound(other).isSubType(self.memberLoBound(member)))
overrideTypeError(pos, member, other, self, true);
break;
default:
if (other.isConstructor())
overrideError(pos, member, other,
"cannot override a class constructor");
if (!normalizedInfo(self, member).isSubType(
normalizedInfo(self, other)))
overrideTypeError(pos, member, other, self, false);
}
}
}
void overrideError(int pos, Symbol member, Symbol other, String msg) {
if (other.type() != Type.ErrorType && member.type() != Type.ErrorType)
unit.error(pos,
"error overriding " + other + other.locationString() +
";\n " + member + member.locationString() + " " + msg);
}
void overrideTypeError(int pos, Symbol member, Symbol other, Type site,
boolean lobound) {
if (other.type() != Type.ErrorType && member.type() != Type.ErrorType) {
Type memberInfo = lobound ? site.memberLoBound(member)
: normalizedInfo(site, member);
Type otherInfo = lobound ? site.memberLoBound(other)
: normalizedInfo(site, other);
unit.error(pos,
member + member.locationString() +
infoString(member, memberInfo, lobound) +
"\n cannot override " + other + other.locationString() +
infoString(other, otherInfo, lobound));
Type.explainTypes(memberInfo, otherInfo);
}
}
Type normalizedInfo(Type site, Symbol sym) {
return site.memberInfo(sym).derefDef();
}
String infoString(Symbol sym, Type symtype, boolean lobound) {
switch (sym.kind) {
case ALIAS: return ", which equals " + symtype;
case TYPE: return " bounded" + (lobound ? " from below" : "") + " by " + symtype;
case VAL: return " of type " + symtype;
default: return "";
}
}
/** compensate for renaming during addition of access functions
*/
Name normalize(Name name) {
return (name.endsWith(Name.fromString("$")))
? name.subName(0, name.length() - 1)
: name;
}
// Basetype Checking --------------------------------------------------------
/** 1. Check that only traits are inherited several times (except if the
* inheriting instance is a compund type).
* 2. Check that later type instances in the base-type sequence
* of a class are subtypes of earlier type instances of the same trait.
* 3. Check that case classes do not inherit from case classes.
* 4. Check that at most one base type is a case-class.
*/
void validateBaseTypes(Symbol clazz) {
validateBaseTypes(clazz, clazz.type().parents(),
new Type[clazz.closure().length], clazz, 0);
}
//where
void validateBaseTypes(Symbol clazz, Type[] tps, Type[] seen,
Symbol caseSeen, int start) {
for (int i = tps.length - 1; i >= start; i--) {
validateBaseTypes(
clazz, tps[i].unalias(), seen, caseSeen, i == 0 ? 0 : 1);
}
}
void validateBaseTypes(Symbol clazz, Type tp, Type[] seen,
Symbol caseSeen, int start) {
Symbol baseclazz = tp.symbol();
if (baseclazz.kind == CLASS) {
int index = clazz.closurePos(baseclazz);
if (index < 0) return;
if (seen[index] != null) {
// check that only uniform classes are inherited several times.
if (!clazz.isCompoundSym() && !baseclazz.isTrait()) {
unit.error(clazz.pos, "illegal inheritance;\n" + clazz +
" inherits " + baseclazz + " twice");
}
// if there are two different type instances of same class
// check that second is a subtype of first.
if (!seen[index].isSubType(tp)) {
String msg = (clazz.isCompoundSym())
? "illegal combination;\n compound type combines"
: "illegal inheritance;\n " + clazz + " inherits";
unit.error(clazz.pos, msg + " different type instances of " +
baseclazz + ":\n" + tp + " and " + seen[index]);
}
}
// check that case classes do not inherit from case classes
if (baseclazz.isCaseClass())
if (caseSeen.isCaseClass())
unit.error(
clazz.pos, "illegal combination of case " +
caseSeen + " and case " + baseclazz + " in one object");
else
caseSeen = baseclazz;
seen[index] = tp;
validateBaseTypes(clazz, tp.parents(), seen, caseSeen, start);
}
}
// Variance Checking --------------------------------------------------------
private final int
ContraVariance = -1,
NoVariance = 0,
CoVariance = 1,
AnyVariance = 2;
private String varianceString(int variance) {
if (variance == 1) return "covariant";
else if (variance == -1) return "contravariant";
else return "invariant";
}
/** The variance of symbol `base' relative to the class which defines `tvar'.
*/
int flip(Symbol base, Symbol tvar) {
Symbol clazz = tvar.owner().constructorClass();
Symbol sym = base;
int flip = CoVariance;
while (sym != clazz && flip != AnyVariance) {
//System.out.println("flip: " + sym + " " + sym.isParameter());//DEBUG
if (sym.isParameter()) flip = -flip;
else if (sym.owner().kind != CLASS) flip = AnyVariance;
else if (sym.kind == ALIAS) flip = NoVariance;
sym = sym.owner();
}
return flip;
}
/** Check variance of type variables in this type
*/
void validateVariance(Symbol base, Type tp, int variance) {
validateVariance(base, tp, tp, variance);
}
void validateVariance(Symbol base, Type all, Type tp, int variance) {
switch (tp) {
case ErrorType:
case AnyType:
case NoType:
case ThisType(Symbol sym):
break;
case SingleType(Type pre, Symbol sym):
validateVariance(base, all, pre, variance);
break;
case TypeRef(Type pre, Symbol sym, Type[] args):
if (sym.variance() != 0) {
int f = flip(base, sym);
if (f != AnyVariance && sym.variance() != f * variance) {
//System.out.println("flip(" + base + "," + sym + ") = " + f);//DEBUG
unit.error(base.pos,
varianceString(sym.variance()) + " " + sym +
" occurs in " + varianceString(f * variance) +
" position in type " + all + " of " + base);
}
}
validateVariance(base, all, pre, variance);
validateVariance(base, all, args, variance, sym.typeParams());
break;
case CompoundType(Type[] parts, Scope members):
validateVariance(base, all, parts, variance);
break;
case MethodType(Symbol[] vparams, Type result):
validateVariance(base, all, result, variance);
break;
case PolyType(Symbol[] tparams, Type result):
validateVariance(base, all, result, variance);
break;
case OverloadedType(Symbol[] alts, Type[] alttypes):
validateVariance(base, all, alttypes, variance);
}
}
void validateVariance(Symbol base, Type all, Type[] tps, int variance) {
for (int i = 0; i < tps.length; i++)
validateVariance(base, all, tps[i], variance);
}
void validateVariance(Symbol base, Type all, Type[] tps, int variance, Symbol[] tparams) {
for (int i = 0; i < tps.length; i++)
validateVariance(base, all, tps[i], variance * tparams[i].variance());
}
// Forward reference checking ---------------------------------------------------
private Scope[] scopes = new Scope[4];
private int[] maxindex = new int[4];
private int[] refpos = new int[4];
private Symbol[] refsym = new Symbol[4];
private int level;
private HashMap symIndex = new HashMap();
private void pushLevel() {
level++;
if (level == scopes.length) {
Scope[] scopes1 = new Scope[scopes.length * 2];
int[] maxindex1 = new int[scopes.length * 2];
int[] refpos1 = new int[scopes.length * 2];
Symbol[] refsym1 = new Symbol[scopes.length * 2];
System.arraycopy(scopes, 0, scopes1, 0, scopes.length);
System.arraycopy(maxindex, 0, maxindex1, 0, scopes.length);
System.arraycopy(refpos, 0, refpos1, 0, scopes.length);
System.arraycopy(refsym, 0, refsym1, 0, scopes.length);
scopes = scopes1;
maxindex = maxindex1;
refpos = refpos1;
refsym = refsym1;
}
scopes[level] = new Scope(scopes[level - 1]);
maxindex[level] = Integer.MIN_VALUE;
}
private void popLevel() {
scopes[level] = null;
level --;
}
private void enterSyms(Tree[] stats) {
for (int i = 0; i < stats.length; i++) {
enterSym(stats[i], i);
}
}
private void enterSym(Tree stat, int index) {
Symbol sym = null;
switch (stat) {
case ClassDef(_, _, _, _, _, _):
sym = stat.symbol().primaryConstructor();
break;
case DefDef(_, _, _, _, _, _):
case ModuleDef(_, _, _, _):
case ValDef(_, _, _, _):
sym = stat.symbol();
}
if (sym != null && sym.isLocal()) {
scopes[level].enter(new Scope.Entry(sym, scopes[level]));
symIndex.put(sym, new Integer(index));
}
}
// Module eliminiation -----------------------------------------------------------
private Tree[] transformModule(Tree tree, int mods, Name name, Tree tpe, Tree.Template templ) {
Symbol sym = tree.symbol();
Tree cdef = gen.ClassDef(sym.moduleClass(), templ);
Tree alloc = gen.New(gen.mkPrimaryConstr(tree.pos, sym.moduleClass()));
if (sym.isGlobalModule()) {
Tree vdef = gen.ValDef(sym, alloc);
return new Tree[]{cdef, vdef};
} else {
// var m$: T = null;
Name varname = Name.fromString(name + "$");
Symbol mvar = new TermSymbol(
tree.pos, varname, sym.owner(), PRIVATE | MUTABLE | SYNTHETIC)
.setInfo(sym.type());
sym.owner().members().enterOrOverload(mvar);
Tree vdef = gen.ValDef(mvar, gen.Ident(tree.pos, defs.NULL));
// { if (null == m$) m$ = new m$class; m$ }
Symbol eqMethod = getUnaryMemberMethod(
sym.type(), Names.EQEQ, defs.ANY_TYPE);
Tree body = gen.Block(new Tree[]{
gen.If(
gen.Apply(
gen.Select(gen.Ident(tree.pos, defs.NULL), eqMethod),
new Tree[]{gen.mkRef(tree.pos, mvar)}),
gen.Assign(gen.mkRef(tree.pos, mvar), alloc),
gen.Block(tree.pos, Tree.EMPTY_ARRAY)),
gen.mkRef(tree.pos, mvar)});
// def m: T = { if (m$ == null[T]) m$ = new m$class; m$ }
sym.updateInfo(Type.PolyType(Symbol.EMPTY_ARRAY, sym.type()));
sym.flags |= STABLE;
Tree ddef = gen.DefDef(sym, body);
// def m_eq(m: T): Unit = { m$ = m }
Name m_eqname = name.append(Names._EQ);
Symbol m_eq = new TermSymbol(
tree.pos, m_eqname, sym.owner(), PRIVATE | SYNTHETIC);
Symbol m_eqarg = new TermSymbol(tree.pos, name, m_eq, 0)
.setType(sym.type());
m_eq.setInfo(
Type.MethodType(new Symbol[] {m_eqarg}, defs.UNIT_TYPE));
Tree m_eqdef = gen.DefDef(m_eq,
gen.Assign(gen.mkRef(tree.pos, mvar), gen.Ident(tree.pos, m_eqarg)));
sym.owner().members().enterOrOverload(m_eq);
return new Tree[]{cdef, vdef, ddef, m_eqdef};
}
}
// Adding case methods --------------------------------------------------------------
private boolean hasImplementation(Symbol clazz, Name name) {
Symbol sym = clazz.info().lookupNonPrivate(name);
return sym.kind == VAL &&
(sym.owner() == clazz ||
!defs.JAVA_OBJECT_CLASS.isSubClass(sym.owner()) &&
(sym.flags & DEFERRED) == 0);
}
private Symbol getMember(Type site, Name name) {
Symbol sym = site.lookupNonPrivate(name);
assert sym.kind == VAL;
return sym;
}
private Symbol getNullaryMemberMethod(Type site, Name name) {
Symbol sym = getMember(site, name);
switch (sym.type()) {
case OverloadedType(Symbol[] alts, Type[] alttypes):
for (int i = 0; i < alts.length; i++) {
if (isNullaryMethod(alttypes[i])) return alts[i];
}
}
assert isNullaryMethod(sym.type())
: "no nullary method " + name + " among " + sym.type() + " at " + site;
return sym;
}
private boolean isNullaryMethod(Type tp) {
return tp.paramSectionCount() == 1 && tp.firstParams().length == 0;
}
private Symbol getUnaryMemberMethod(Type site, Name name, Type paramtype) {
Symbol sym = getMember(site, name);
switch (sym.type()) {
case OverloadedType(Symbol[] alts, Type[] alttypes):
for (int i = 0; i < alts.length; i++) {
if (hasParam(alttypes[i], paramtype)) return alts[i];
}
}
assert hasParam(sym.type(), paramtype)
: "no (" + paramtype + ")-method " + name + " among " + sym.type() + " at " + site;
return sym;
}
private boolean hasParam(Type tp, Type paramtype) {
Symbol[] params = tp.firstParams();
return params.length == 1 && paramtype.isSubType(params[0].type());
}
private Tree[] caseFields(ClassSymbol clazz) {
Symbol[] vparams = clazz.primaryConstructor().type().firstParams();
Tree[] fields = new Tree[vparams.length];
for (int i = 0; i < fields.length; i++) {
fields[i] = gen.mkRef(
clazz.pos, clazz.thisType(), clazz.caseFieldAccessor(i));
}
return fields;
}
private Tree toStringMethod(ClassSymbol clazz) {
Symbol toStringSym = new TermSymbol(
clazz.pos, Names.toString, clazz, OVERRIDE)
.setInfo(defs.TOSTRING.type());
clazz.info().members().enter(toStringSym);
Tree[] fields = caseFields(clazz);
Tree body;
if (fields.length == 0) {
body = gen.mkStringLit(
clazz.pos, NameTransformer.decode(clazz.name).toString());
} else {
body = gen.mkStringLit(
clazz.pos, NameTransformer.decode(clazz.name).toString() + "(");
for (int i = 0; i < fields.length; i++) {
String str = (i == fields.length - 1) ? ")" : ",";
body = gen.Apply(
gen.Select(body, defs.STRING_PLUS_ANY),
new Tree[]{fields[i]});
body = gen.Apply(
gen.Select(body, defs.STRING_PLUS_ANY),
new Tree[]{gen.mkStringLit(clazz.pos, str)});
}
}
return gen.DefDef(toStringSym, body);
}
private Tree equalsMethod(ClassSymbol clazz) {
Symbol equalsSym = new TermSymbol(clazz.pos, Names.equals, clazz, OVERRIDE);
Symbol equalsParam =
new TermSymbol(clazz.pos, Names.that, equalsSym, PARAM)
.setInfo(defs.ANY_TYPE);
equalsSym.setInfo(
Type.MethodType(new Symbol[]{equalsParam}, defs.BOOLEAN_TYPE));
clazz.info().members().enter(equalsSym);
Tree[] fields = caseFields(clazz);
Type testtp = clazz.type();
{
Symbol[] tparams = clazz.typeParams();
if (tparams.length != 0) {
Type[] targs = new Type[tparams.length];
for (int i = 0; i < targs.length; i++)
targs[i] = defs.ANY_TYPE;
testtp = testtp.subst(tparams, targs);
}
}
// if (that is C) {...
Tree cond = gen.TypeApply(
gen.Select(
gen.mkRef(clazz.pos, Type.localThisType, equalsParam),
defs.IS),
new Tree[]{gen.mkType(clazz.pos, testtp)});
Tree thenpart;
if (fields.length == 0) {
thenpart = gen.mkBooleanLit(clazz.pos, true);
} else {
// val that1 = that as C;
Tree cast = gen.TypeApply(
gen.Select(
gen.mkRef(clazz.pos, Type.localThisType, equalsParam),
defs.AS),
new Tree[]{gen.mkType(clazz.pos, testtp)});
Symbol that1sym = new TermSymbol(clazz.pos, Names.that1, equalsSym, 0)
.setType(testtp);
Tree that1def = gen.ValDef(that1sym, cast);
// this.elem_1 == that1.elem_1 && ... && this.elem_n == that1.elem_n
Tree cmp = eqOp(
fields[0],
qualCaseField(clazz,
gen.mkRef(clazz.pos, Type.localThisType, that1sym), 0));
for (int i = 1; i < fields.length; i++) {
cmp = gen.Apply(
gen.Select(cmp, defs.BOOLEAN_AND()),
new Tree[]{
eqOp(
fields[i],
qualCaseField(clazz,
gen.mkRef(clazz.pos, Type.localThisType, that1sym), i))});
}
thenpart = gen.Block(new Tree[]{that1def, cmp});
}
Tree body = gen.If(cond, thenpart, gen.mkBooleanLit(clazz.pos, false));
return gen.DefDef(equalsSym, body);
}
//where
private Tree eqOp(Tree l, Tree r) {
Symbol eqMethod = getUnaryMemberMethod(l.type, Names.EQEQ, r.type);
return gen.Apply(gen.Select(l, eqMethod), new Tree[]{r});
}
private Tree qualCaseField(ClassSymbol clazz, Tree qual, int i) {
return gen.Select(qual, clazz.caseFieldAccessor(i));
}
private Tree tagMethod(ClassSymbol clazz) {
Symbol tagSym = new TermSymbol(
clazz.pos, Names.tag, clazz,
clazz.isSubClass(defs.OBJECT_CLASS) ? OVERRIDE : 0)
.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, defs.INT_TYPE));
clazz.info().members().enter(tagSym);
return gen.DefDef(tagSym, gen.mkIntLit(clazz.pos, clazz.tag()));
}
private Tree hashCodeMethod(ClassSymbol clazz) {
Symbol hashCodeSym = new TermSymbol(
clazz.pos, Names.hashCode, clazz, OVERRIDE)
.setInfo(defs.HASHCODE.type());
clazz.info().members().enter(hashCodeSym);
Tree[] fields = caseFields(clazz);
Symbol getClassMethod = getNullaryMemberMethod(clazz.type(), Names.getClass);
Symbol addMethod = getUnaryMemberMethod(
defs.INT_TYPE, Names.ADD, defs.INT_TYPE);
Symbol mulMethod = getUnaryMemberMethod(
defs.INT_TYPE, Names.MUL, defs.INT_TYPE);
Tree body =
gen.Apply(
gen.Select(
gen.Apply(
gen.mkRef(clazz.pos, clazz.thisType(), getClassMethod),
Tree.EMPTY_ARRAY),
getNullaryMemberMethod(getClassMethod.type().resultType(), Names.hashCode)),
Tree.EMPTY_ARRAY);
for (int i = 0; i < fields.length; i++) {
Tree operand = gen.Apply(
gen.Select(
fields[i],
getNullaryMemberMethod(fields[i].type, Names.hashCode)),
Tree.EMPTY_ARRAY);
body =
gen.Apply(
gen.Select(
gen.Apply(
gen.Select(body, mulMethod),
new Tree[]{gen.mkIntLit(clazz.pos, 41)}),
addMethod),
new Tree[]{operand});
}
return gen.DefDef(hashCodeSym, body);
}
// where
private Template addCaseMethods(Template templ, Symbol sym) {
if (sym.kind == CLASS && (sym.flags & CASE) != 0) {
Tree[] body1 = addCaseMethods(templ.body, (ClassSymbol) sym);
return copy.Template(templ, templ.parents, body1);
}
return templ;
}
private Tree[] addCaseMethods(Tree[] stats, ClassSymbol clazz) {
TreeList ts = new TreeList();
if (!hasImplementation(clazz, Names.toString))
ts.append(toStringMethod(clazz));
if (!hasImplementation(clazz, Names.equals))
ts.append(equalsMethod(clazz));
if (!hasImplementation(clazz, Names.hashCode))
ts.append(hashCodeMethod(clazz));
ts.append(tagMethod(clazz));
if (ts.length() > 0) {
Tree[] stats1 = new Tree[stats.length + ts.length()];
System.arraycopy(stats, 0, stats1, 0, stats.length);
ts.copyTo(stats1, stats.length);
return stats1;
} else {
return stats;
}
}
// Convert case factory calls to constructor calls ---------------------------
/** Tree represents an application of a constructor method of a case class
* (whose name is a term name). Convert this tree to application of
* the case classe's primary constructor `constr'.
*/
private Tree toConstructor(Tree tree, Symbol constr) {
switch (tree) {
case Apply(Tree fn, Tree[] args):
return copy.Apply(tree, toConstructor1(fn, constr), args);
default:
return gen.Apply(
tree.pos, toConstructor1(tree, constr), Tree.EMPTY_ARRAY);
}
}
//where
private Tree toConstructor1(Tree tree, Symbol constr) {
switch (tree) {
case TypeApply(Tree fn, Tree[] args):
return toMethodType(
copy.TypeApply(tree, toConstructor1(fn, constr), args));
case Ident(_):
return toMethodType(
copy.Ident(tree, constr));
case Select(Tree qual, _):
return toMethodType(
copy.Select(tree, constr, qual));
default:
throw new ApplicationError();
}
}
private Tree toMethodType(Tree tree) {
Type tp = toMethodType(tree.type);
if (tp == tree.type) return tree;
else return tree.duplicate().setType(tp);
}
private Type toMethodType(Type tp) {
switch (tp) {
case MethodType(_, _):
return tp;
case PolyType(Symbol[] tparams, Type restp):
Type restp1 = toMethodType(restp);
if (restp == restp) return tp;
else return Type.PolyType(tparams, restp1);
default:
return Type.MethodType(Symbol.EMPTY_ARRAY, tp);
}
}
// Bounds checking -----------------------------------------------------------
private void checkBounds(int pos, Symbol[] tparams, Type[] argtypes) {
if (tparams.length == argtypes.length) {
try {
infer.checkBounds(tparams, argtypes, "");
} catch (Type.Error ex) {
unit.error(pos, ex.msg);
}
}
}
// Type node eliminiation ------------------------------------------------------
private Tree elimTypeNode(Tree tree) {
if (tree.isType() && !tree.isMissing())
return gen.mkType(tree.pos, tree.type);
else
return tree;
}
// Transformation ---------------------------------------------------------------
public Tree[] transformStats(Tree[] stats) {
pushLevel();
enterSyms(stats);
int i = 0;
while (i < stats.length) {
Object stat1 = transformStat(stats[i], i);
if (stat1 instanceof Tree) {
stats[i] = (Tree) stat1;
i = i + 1;
} else {
Tree[] newstats = (Tree[]) stat1;
Tree[] stats1 = new Tree[stats.length - 1 + newstats.length];
System.arraycopy(stats, 0, stats1, 0, i);
System.arraycopy(newstats, 0, stats1, i, newstats.length);
System.arraycopy(stats, i + 1, stats1, i + newstats.length,
stats.length - i - 1);
stats = stats1;
i = i + newstats.length;
}
}
popLevel();
return stats;
}
public Object transformStat(Tree tree, int index) {
switch (tree) {
case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ):
return transform(transformModule(tree, mods, name, tpe, templ));
case ValDef(int mods, Name name, Tree tpe, Tree rhs):
Symbol sym = tree.symbol();
validateVariance(
sym, sym.type(),
((sym.flags & MUTABLE) != 0) ? NoVariance : CoVariance);
Tree tree1 = transform(tree);
//todo: handle variables
if (sym.isLocal() && !sym.isModule() && index <= maxindex[level]) {
if (Global.instance.debug)
System.out.println(refsym[level] + ":" + refsym[level].type());
String kind = ((sym.flags & MUTABLE) != 0) ? "variable" : "value";
unit.error(
refpos[level],
"forward reference extends over definition of " + kind + " " +
normalize(name));
}
return tree1;
default:
return transform(tree);
}
}
public Tree transform(Tree tree) {
Symbol sym = tree.symbol();
switch (tree) {
case ClassDef(_, _, Tree.AbsTypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree.Template templ):
Symbol enclClassPrev = enclClass;
enclClass = sym;
validateVariance(sym, sym.info(), CoVariance);
validateVariance(sym, sym.typeOfThis(), CoVariance);
Tree tree1 = super.transform(
copy.ClassDef(tree, tree.symbol(), tparams, vparams, tpe, addCaseMethods(templ, tree.symbol())));
enclClass = enclClassPrev;
return tree1;
case DefDef(_, _, _, _, _, _):
validateVariance(sym, sym.type(), CoVariance);
return super.transform(tree);
case ValDef(_, _, _, _):
validateVariance(
sym, sym.type(),
((sym.flags & MUTABLE) != 0) ? NoVariance : CoVariance);
return super.transform(tree);
case AbsTypeDef(_, _, _, _):
validateVariance(sym, sym.info(), CoVariance);
validateVariance(sym, sym.loBound(), ContraVariance);
return super.transform(tree);
case AliasTypeDef(_, _, _, _):
validateVariance(sym, sym.info(), CoVariance);
return super.transform(tree);
case Template(Tree[] bases, Tree[] body):
Tree[] bases1 = transform(bases);
Tree[] body1 = transformStats(body);
if (sym.kind == VAL) {
Symbol owner = tree.symbol().owner();
validateBaseTypes(owner);
checkAllOverrides(tree.pos, owner);
}
return copy.Template(tree, bases1, body1);
case Block(Tree[] stats):
Tree[] stats1 = transformStats(stats);
return copy.Block(tree, stats1);
case This(_):
return tree;
case PackageDef(Tree pkg, Template packaged):
return copy.PackageDef(tree, pkg, super.transform(packaged));
case TypeApply(Tree fn, Tree[] args):
switch (fn.type) {
case PolyType(Symbol[] tparams, Type restp):
checkBounds(tree.pos, tparams, Tree.typeOf(args));
}
return super.transform(tree);
case Apply(Tree fn, Tree[] args):
// convert case methods to new's
Symbol fsym = TreeInfo.methSymbol(fn);
assert fsym != Symbol.NONE : tree;
if (fsym != null && fsym.isMethod() && !fsym.isConstructor() &&
(fsym.flags & CASE) != 0) {
Symbol constr = fsym.type().resultType().symbol().primaryConstructor();
tree = gen.New(toConstructor(tree, constr));
}
return super.transform(tree);
case AppliedType(Tree tpe, Tree[] args):
Symbol[] tparams = tpe.type.symbol().typeParams();
checkBounds(tree.pos, tparams, Tree.typeOf(args));
return elimTypeNode(super.transform(tree));
case CompoundType(_, _):
Symbol clazz = tree.type.symbol();
validateBaseTypes(clazz);
checkAllOverrides(tree.pos, clazz);
return elimTypeNode(super.transform(tree));
case Ident(Name name):
if (name == TypeNames.WILDCARD_STAR)
return tree;
if( sym == defs.PATTERN_WILDCARD )
return elimTypeNode(tree);
//System.out.println("name: "+name);
Scope.Entry e = scopes[level].lookupEntry(name);
//System.out.println("sym: "+sym);
if (sym.isLocal() && sym == e.sym) {
int i = level;
while (scopes[i] != e.owner) i--;
int symindex = ((Integer) symIndex.get(tree.symbol())).intValue();
if (maxindex[i] < symindex) {
refpos[i] = tree.pos;
refsym[i] = e.sym;
maxindex[i] = symindex;
}
}
sym.flags |= ACCESSED;
return elimTypeNode(tree);
case Select(Tree qual, Name name):
sym.flags |= ACCESSED;
if (!TreeInfo.isSelf(qual, enclClass))
sym.flags |= SELECTOR;
if ((sym.flags & DEFERRED) != 0) {
switch (qual) {
case Super(Name qualifier, Name mixin):
Symbol sym1 = enclClass.thisSym().info().lookup(sym.name);
if (mixin != TypeNames.EMPTY || !isIncomplete(sym1))
unit.error(
tree.pos,
"symbol accessed from super may not be abstract");
}
}
return elimTypeNode(super.transform(tree));
default:
return elimTypeNode(super.transform(tree));
}
}
}