summaryrefslogtreecommitdiff
path: root/sources/scalac/typechecker/RefCheck.java
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scalac/typechecker/RefCheck.java')
-rw-r--r--sources/scalac/typechecker/RefCheck.java205
1 files changed, 193 insertions, 12 deletions
diff --git a/sources/scalac/typechecker/RefCheck.java b/sources/scalac/typechecker/RefCheck.java
index d6a3515eea..d18daac17f 100644
--- a/sources/scalac/typechecker/RefCheck.java
+++ b/sources/scalac/typechecker/RefCheck.java
@@ -9,6 +9,7 @@ import java.util.HashMap;
import scalac.*;
import scalac.util.*;
import scalac.ast.*;
+import scalac.ast.printer.*;
import scalac.symtab.*;
import Tree.*;
@@ -183,8 +184,8 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
}
private Tree[] caseFields(ClassSymbol clazz) {
- Symbol[] tparams = clazz.typeParams();
- Tree[] fields = new Tree[clazz.constructor().type().firstParams().length];
+ Symbol[] vparams = clazz.constructor().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));
@@ -193,6 +194,10 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
}
private Tree toStringMethod(ClassSymbol clazz) {
+ Symbol toStringSym = new TermSymbol(
+ clazz.pos, Names.toString, clazz, OVERRIDE)
+ .setInfo(definitions.TOSTRING.type());
+ clazz.info().members().enter(toStringSym);
Tree[] fields = caseFields(clazz);
Tree body;
if (fields.length == 0) {
@@ -210,20 +215,137 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
new Tree[]{gen.mkStringLit(clazz.pos, (i == fields.length - 1) ? ")" : ",")});
}
}
- Symbol toStringSym = new TermSymbol(
- clazz.pos, Names.toString, clazz, OVERRIDE)
- .setInfo(definitions.TOSTRING.type());
- clazz.info().members().enter(toStringSym);
return gen.DefDef(clazz.pos, toStringSym, body);
}
private Tree equalsMethod(ClassSymbol clazz) {
- return Tree.Empty;
+ Symbol equalsSym = new TermSymbol(clazz.pos, Names.EQEQ, clazz, OVERRIDE);
+ Symbol equalsParam =
+ new TermSymbol(clazz.pos, Names.that, equalsSym, PARAM)
+ .setInfo(definitions.ANY_TYPE);
+ equalsSym.setInfo(
+ Type.MethodType(new Symbol[]{equalsParam}, definitions.BOOLEAN_TYPE));
+ clazz.info().members().enter(equalsSym);
+ Tree[] fields = caseFields(clazz);
+ Tree[] patargs = patternVars(clazz, equalsSym);
+ Type constrtype = clazz.constructor().type();
+ switch (constrtype) {
+ case PolyType(Symbol[] tparams, Type restp):
+ Type[] targs = new Type[tparams.length];
+ for (int i = 0; i < targs.length; i++)
+ targs[i] = definitions.ANY_TYPE;
+ constrtype = restp.subst(tparams, targs);
+ }
+ Tree pattern = make.Apply(
+ clazz.pos, gen.mkType(clazz.pos, constrtype), patargs)
+ .setType(clazz.type());
+ Tree rhs;
+ if (fields.length == 0) {
+ rhs = gen.mkBooleanLit(clazz.pos, true);
+ } else {
+ rhs = eqOp(fields[0], patargs[0]);
+ for (int i = 1; i < fields.length; i++) {
+ rhs = gen.Apply(
+ gen.Select(rhs, definitions.AMPAMP()),
+ new Tree[]{eqOp(fields[i], patargs[i])});
+ }
+ }
+ CaseDef case1 = (Tree.CaseDef) make.CaseDef(
+ clazz.pos, pattern, Tree.Empty, rhs)
+ .setType(definitions.BOOLEAN_TYPE);
+ CaseDef case2 = (Tree.CaseDef) make.CaseDef(clazz.pos,
+ patternVar(clazz.pos, Names.WILDCARD, equalsSym),
+ Tree.Empty,
+ gen.mkBooleanLit(clazz.pos, false))
+ .setType(definitions.BOOLEAN_TYPE);
+ Tree body = make.Apply(clazz.pos,
+ gen.Select(
+ gen.mkRef(clazz.pos, Type.localThisType, equalsParam),
+ definitions.MATCH),
+ new Tree[]{make.Visitor(clazz.pos, new CaseDef[]{case1, case2})
+ .setType(definitions.BOOLEAN_TYPE)})
+ .setType(definitions.BOOLEAN_TYPE);
+ return gen.DefDef(clazz.pos, equalsSym, body);
}
+ //where
+ private Tree patternVar(int pos, Name name, Symbol owner) {
+ return make.Ident(pos, name)
+ .setSymbol(new TermSymbol(pos, name, owner, 0)
+ .setType(definitions.ANY_TYPE))
+ .setType(definitions.ANY_TYPE);
+ }
+
+ private Tree[] patternVars(ClassSymbol clazz, Symbol owner) {
+ Symbol[] vparams = clazz.constructor().type().firstParams();
+ Tree[] pats = new Tree[vparams.length];
+ for (int i = 0; i < pats.length; i++) {
+ pats[i] = patternVar(clazz.pos, vparams[i].name, owner);
+ }
+ return pats;
+ }
+
+ private Tree eqOp(Tree l, Tree r) {
+ return gen.Apply(gen.Select(l, definitions.EQEQ), new Tree[]{r});
+ }
private Tree hashCodeMethod(ClassSymbol clazz) {
- return Tree.Empty;
+ Symbol hashCodeSym = new TermSymbol(
+ clazz.pos, Names.hashCode, clazz, OVERRIDE)
+ .setInfo(definitions.HASHCODE.type());
+ clazz.info().members().enter(hashCodeSym);
+ Tree[] fields = caseFields(clazz);
+ Symbol getClassSym = getMember(clazz.type(), Names.getClass);
+ Symbol addSym = intMethod(Names.ADD);
+ Symbol mulSym = intMethod(Names.MUL);
+ Tree body =
+ gen.Apply(
+ gen.Select(
+ gen.Apply(
+ gen.mkRef(clazz.pos, clazz.thisType(), getClassSym),
+ Tree.EMPTY_ARRAY),
+ getMember(getClassSym.type().resultType(), Names.hashCode)),
+ Tree.EMPTY_ARRAY);
+ for (int i = 0; i < fields.length; i++) {
+ Tree operand = gen.Apply(
+ gen.Select(
+ fields[i],
+ getMember(fields[i].type, Names.hashCode)),
+ Tree.EMPTY_ARRAY);
+ body =
+ gen.Apply(
+ gen.Select(
+ gen.Apply(
+ gen.Select(body, mulSym),
+ new Tree[]{gen.mkIntLit(clazz.pos, 41)}),
+ addSym),
+ new Tree[]{operand});
+ }
+ return gen.DefDef(clazz.pos, hashCodeSym, body);
}
+ // where
+ private Symbol getMember(Type site, Name name) {
+ Symbol sym = site.lookupNonPrivate(name);
+ assert sym.kind == VAL;
+ return sym;
+ }
+
+ private Symbol intMethod(Name name) {
+ Symbol sym = getMember(definitions.INT_TYPE, name);
+ switch (sym.type()) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ for (int i = 0; i < alts.length; i++) {
+ if (hasIntParam(alttypes[i])) return alts[i];
+ }
+ }
+ assert hasIntParam(sym.type()) : "no int method among " + sym.type();
+ return sym;
+ }
+
+ private boolean hasIntParam(Type tp) {
+ Symbol[] params = tp.firstParams();
+ return params.length == 1 &&
+ params[0].type().isSameAs(definitions.INT_TYPE);
+ }
private Template addCaseMethods(Template templ, Symbol sym) {
if (sym.kind == CLASS && (sym.flags & CASE) != 0) {
@@ -237,12 +359,10 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
TreeList ts = new TreeList();
if (!hasImplementation(clazz, Names.toString))
ts.append(toStringMethod(clazz));
-/*
if (!hasImplementation(clazz, Names.EQEQ))
ts.append(equalsMethod(clazz));
if (!hasImplementation(clazz, Names.hashCode))
ts.append(hashCodeMethod(clazz));
-*/
if (ts.length() > 0) {
Tree[] stats1 = new Tree[stats.length + ts.length()];
System.arraycopy(stats, 0, stats1, 0, stats.length);
@@ -253,6 +373,67 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
}
}
+ public Tree convertCaseFactoryCall(Tree tree) {
+ Symbol fsym = TreeInfo.methSymbol(tree);
+ if (fsym != null && fsym.isMethod() && !fsym.isConstructor() &&
+ (fsym.flags & CASE) != 0) {
+ // convert case methods to new's
+ Symbol constr = fsym.owner().info()
+ .lookup(fsym.name.toTypeName()).constructor();
+ return gen.New(toConstructor(tree, constr));
+ } else {
+ return tree;
+ }
+ }
+ //where
+ /** 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);
+ }
+ }
+
+ 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(Name name):
+ return toMethodType(
+ copy.Ident(tree, constr.name).setSymbol(constr));
+ case Select(Tree qual, Name name):
+ return toMethodType(
+ copy.Select(tree, qual, constr.name).setSymbol(constr));
+ 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);
+ }
+ }
/** The main checking functions
*/
@@ -314,10 +495,10 @@ public class RefCheck extends Transformer implements Modifiers, Kinds {
maxindex[i] = symindex;
}
}
- tree1 = tree;
+ tree1 = convertCaseFactoryCall(tree);
break;
default:
- tree1 = super.transform(tree);
+ tree1 = super.transform(convertCaseFactoryCall(tree));
}
if (tree1.isType() && !tree1.isMissing())
tree1 = gen.mkType(tree1.pos, tree1.type);