/* Copyright (C) 2011, Christoph Reichenbach
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
package com.sun.tools.javac.comp;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.pql.PQLBody;
import com.sun.tools.javac.tree.pql.PQLMagicSubexpr;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.HashSet;
import edu.umass.pql.*;
/**
* Infer constants, types, and inner and outer pql queries whilst reporting errors.
*
* This pass is the PQL equivalent for Attr. It does the following:
* (a) Determine all branches of the base node that are logical constants, and issue errors for all branches that are not constants but contain AST fragments that we don't support.
* (b) For all non-constant parts, perform type inference.
* (c) For all `inner queries', i.e., PQLExpr children that are NOT logical constants (wrt the base node), assert that type inference produced some type.
*/
public class PQLAttr extends JCTree.Visitor
{
public static class Info
{
// inner expressions
LinkedList<PQLExpr> inner_pq = new LinkedList<PQLExpr>();
LinkedList<PQLExpr> outer_pq = new LinkedList<PQLExpr>();
LinkedList<JCExpression> constants = new LinkedList<JCExpression>();
JCTree one = null; // integer one, if observed
Info()
{
}
}
public Info info = new Info();
private HashSet<Symbol> current_variables; // known variables in the current context
private HashSet<Symbol> observed_variables = new HashSet<Symbol>(); // variables observed in subexprs
public static Type
boxRecursively(Types types, Type base_type)
{
final List<Type> old_args = base_type.getTypeArguments();
if (old_args == null || old_args == List.<Type>nil()) {
if (base_type.isPrimitive())
return types.boxedClass(base_type).type;
else
return base_type;
}
final ListBuffer<Type> new_args = new ListBuffer<Type>();
for (Type ty : old_args) {
new_args.add(boxRecursively(types, ty));
}
return new Type.ClassType(base_type.getEnclosingType(), new_args.toList(), base_type.tsym);
}
private Type
boxRecursively(Type type)
{
return boxRecursively(attr.types, type);
}
public Type
setTypeOf(Type elemtype)
{
return attr.types.cloneWithFreshArgs(attr.syms.setType, elemtype);
}
public Type
collectionTypeOf(Type elemtype)
{
return attr.types.cloneWithFreshArgs(attr.syms.collectionType, elemtype);
}
public Type
mapTypeOf(Type keytype, Type valuetype)
{
return new Type.MagicMapType(keytype, valuetype);
//applyTypeTo(attr.syms.mapType, keytype, valuetype);
}
public Type
javaUtilMapTypeOf(Type keytype, Type valuetype)
{
return attr.types.cloneWithFreshArgs(attr.syms.mapType, keytype, valuetype);
}
boolean
isConstantX()
{
for (Symbol sym : this.observed_variables)
if (this.current_variables.contains(sym)) {
//System.err.println("[c] is-constant: {"+sym+"} in " + this.current_variables);
return false;
}
return true;
}
boolean
isConstant()
{
boolean result = isConstantX();
return result;
}
public Attr attr;
private PQLAttr(Attr attr, HashSet<Symbol> symbols)
{
this.attr = attr;
this.current_variables = symbols;
}
public void
addConstant(JCExpression tree)
{
//System.err.println("[c] ++>> " + tree);
// if (tree.toString().equals("11"))
// throw new RuntimeException("Whaaa?");
if (tree != null) {
this.info.constants.add(tree);
attr.pt = Type.noType;
tree.accept(attr); // name and type analysis, if not done yet
}
}
public void
handleLists(List<? extends JCTree> ... elements)
{
int size = 0;
for (List<? extends JCTree> l : elements)
size += l.size();
JCTree[] temp = new JCTree[size];
int pos = 0;
for (List<? extends JCTree> l : elements)
for (JCTree t : l)
temp[pos++] = t;
handle(temp);
}
// static int depth = 0;
public void
handle(JCTree... elements)
{
// System.err.print("handle: [ ");
// for (JCTree t : elements)
// try {
// System.err.print("{" + t + "} " );
// } catch (Throwable _) {
// System.err.print("(?"+t.getClass()+"?)");
// }
// System.err.println("]");
boolean has_constants = false;
boolean has_variables = false;
// System.err.print("[c]");
// for (int i = 0; i < depth; i++)
// System.err.print(" ");
// System.err.print(" {");
// for (int i = 0; i < elements.length; i++) {
// if (i > 0)
// System.err.print(", ");
// System.err.print(elements[i]);
// }
// System.err.println("}");
// ++depth;
HashSet<Symbol> observed_symbol_accumulator = this.observed_variables;
// System.err.println("[c] current-vars = " + current_variables);
// System.err.println("[c] observed-vars = " + observed_variables);
for (int offset = 0; offset < elements.length; offset++)
if (elements[offset] != null) {
elements[offset].accept(this);
//System.err.println("[c] #" + offset + " is-constant=" + this.isConstant() + " hc=" + has_constants + " hv=" + has_variables);
if (this.isConstant()) {
has_constants = true;
if (has_variables) // we're going to draw the line to constantness at this expression, so the constant branches must be remembered
// [1] hypothesis: all constants are JCExpressions
// If not, then that will require a couple of changes elsewhere but it shouldn't be a big deal.
addConstant((JCExpression) elements[offset]);
} else {
if (!has_variables) {
has_variables = true;
// If we've only seen constants so far and only just saw our first variable, we have to go back
// and tag all observed constants
for (int i = 0; i < offset; i++)
// hypothesis: all constants are JCExpressions (see [1] above)
addConstant((JCExpression) elements[i]);
}
}
if (offset + 1 < elements.length) {
if (observed_symbol_accumulator == this.observed_variables) {
this.observed_variables = new HashSet<Symbol>();
} else {
observed_symbol_accumulator.addAll(this.observed_variables);
this.observed_variables.clear();
}
} else if (offset > 0)
observed_symbol_accumulator.addAll(this.observed_variables);
}
this.observed_variables = observed_symbol_accumulator;
for (JCTree t : elements)
if (t != null)
inferType(t);
// --depth;
// System.err.print("[c] ");
// for (int i = 0; i < depth; i++)
// System.err.print(" ");
// System.err.println("has_constants=" + has_constants + "; has_variables = " + has_variables + "; isConstant= " + isConstantX() + "; accum=" + observed_symbol_accumulator);
}
/**
* Attribute tree and collect all expressions that do not depend on the specified variable symbols
*/
public static Info
attribute(Attr attr, PQLExpr tree)
{
final ArrayList<Symbol> variable_symbols = new ArrayList<Symbol>();
HashSet<Symbol> symbols = new HashSet<Symbol>();
for (JCVariableDecl decl: tree.decls)
symbols.add(decl.sym);
final PQLAttr pc = new PQLAttr(attr, symbols);
tree.accept(pc);
pc.solveTypes(tree);
// if (pc.isConstant())
// pc.addConstant(tree);
if (tree.type == null)
pc.inferType(tree);
return pc.info;
}
// --------------------------------------------------------------------------------
public void visitBlock(JCBlock that)
{
handleLists(that.stats);
}
public void visitLabelled(JCLabeledStatement that)
{
handle(that.body);
}
public void visitSwitch(JCSwitch that)
{
handleLists(List.of(that.selector), that.cases);
}
public void visitCase(JCCase that)
{
handleLists(List.of(that.pat), that.stats);
}
public void visitSynchronized(JCSynchronized that)
{
handle(that.lock, that.body);
}
public void visitTry(JCTry that)
{
handleLists(List.of(that.body, that.finalizer), that.catchers);
}
public void visitCatch(JCCatch that)
{
handle(that.param, that.body);
}
public void visitConditional(JCConditional that)
{
handle(that.cond, that.truepart, that.falsepart);
}
public void visitIf(JCIf that)
{
handle(that.cond, that.thenpart, that.elsepart);
}
public void visitExec(JCExpressionStatement that)
{
handle(that.expr);
}
public void visitReturn(JCReturn that)
{
handle(that.expr);
}
public void visitThrow(JCThrow that)
{
handle(that.expr);
}
public void visitAssert(JCAssert that)
{
handle(that.cond, that.detail);
}
public void visitApply(JCMethodInvocation that)
{
final boolean is_magic = PQLMagicSubexpr.isMagic(that);
if (!is_magic)
disallowQueryVariables();
List<JCExpression> args = that.args;
if (that.meth instanceof JCFieldAccess)
args = args.prepend(((JCFieldAccess) that.meth).selected);
handleLists(args);
if (!is_magic)
reallowQueryVariables();
}
int query_variables_disallowed = 0;
void disallowQueryVariables() { ++query_variables_disallowed; }
void reallowQueryVariables() { --query_variables_disallowed; }
boolean queryVariablesAllowed() { return query_variables_disallowed == 0; }
public void visitNewClass(JCNewClass that)
{
disallowQueryVariables();
handleLists(List.<JCTree>of(that.encl), that.args);
reallowQueryVariables();
}
public void visitNewArray(JCNewArray that)
{
disallowQueryVariables();
handleLists(that.dims, that.elems);
reallowQueryVariables();
}
public void visitParens(JCParens that)
{
handle(that.expr);
}
public void visitAssign(JCAssign that)
{
handle(that.lhs, that.rhs);
}
public void visitAssignop(JCAssignOp that)
{
handle(that.lhs, that.rhs);
}
public void visitUnary(JCUnary that)
{
handle(that.arg);
}
public void visitBinary(JCBinary that)
{
handle(that.lhs, that.rhs);
}
public void visitTypeCast(JCTypeCast that)
{
handle(that.expr);
}
public void visitTypeTest(JCInstanceOf that)
{
handle(that.expr);
that.clazz.type = attr.chk.checkReifiableReferenceType(that.clazz.pos(), attr.attribType(that.clazz, attr.env));
}
public void visitIndexed(JCArrayAccess that)
{
handle(that.indexed, that.index);
}
public void visitSelect(JCFieldAccess that)
{
handle(that.selected);
}
public void visitIdent(JCIdent that)
{
attr.pt = Type.noType;
that.accept(attr); // name and type analysis
if (!queryVariablesAllowed()
&& that.sym.isQueryVariable()) {
attr.log.error(that.pos(), "query.var.in.subexpr");
}
this.observed_variables.add(that.sym);
}
public void visitPQLExpr(PQLExpr that)
{
if (that.default_expr != null) {
handle(that.default_expr); // process before locally quantified variables are considered
if (this.isConstant())
addConstant(that.default_expr);
this.observed_variables.clear();
}
final Env<AttrContext> old_env = attr.env;
// Enter scope:
final Env<AttrContext> localEnv = attr.memberEnter.queryEnv(that, attr.env);
final Symbol.QuantificationQuasiSymbol qqs = new Symbol.QuantificationQuasiSymbol(that, attr.env.info.scope.owner);
for (JCVariableDecl decl : that.decls) {
this.current_variables.add(decl.sym);
Type ty = null;
// resolve types, where present
if (decl.vartype != null) {
ty = attr.attribType(decl.vartype, localEnv);
}
attr.attribStat(decl, localEnv);
if (decl.type == null) {
decl.type = ty;
decl.sym.type = ty;
}
decl.sym.owner = qqs;
localEnv.info.scope.enter(decl.sym);
}
attr.env = localEnv;
handle(that.expr);
// for (JCVariableDecl decl : that.decls) {
// this.current_variables.remove(decl.sym);
// try {
// decl.sym.type = decl.sym.type.elimQTyVars();
// } catch (Type.CouldNotInferTypeException exn) {
// exn.log(attr.log, decl.pos(), decl.sym);
// }
// }
if (this.isConstant())
this.info.outer_pq.add(that);
else
this.info.inner_pq.add(that);
// Leave scope:
localEnv.info.scope.leave();
attr.env = old_env;
}
public void visitTree(JCTree that)
{
}
public void visitSkip(JCSkip that) { visitTree(that); }
public void visitBreak(JCBreak that) { visitTree(that); }
public void visitContinue(JCContinue that) { visitTree(that); }
public void visitClassDef(JCClassDecl that) { visitTree(that); }
public void visitTopLevel(JCCompilationUnit that) { err(that); }
public void visitImport(JCImport that) { err(that); }
public void visitMethodDef(JCMethodDecl that) { err(that); }
public void visitVarDef(JCVariableDecl that) { err(that); }
public void visitDoLoop(JCDoWhileLoop that) { err(that); }
public void visitWhileLoop(JCWhileLoop that) { err(that); }
public void visitForLoop(JCForLoop that) { err(that); }
public void visitForeachLoop(JCEnhancedForLoop that) { err(that); }
public void visitLetExpr(LetExpr that) { err(that); }
public void err(JCTree that) { throw new RuntimeException("PQLAttr: Unsupported JCTree within PQL query: " + that.getClass() + ": " + that); }
// ----------------------------------------
// Type inference
PQLTypeInferencer type_inferencer = new PQLTypeInferencer();
public void
inferType(JCTree t)
{
try {
if (t.type == null)
type_inferencer.infer(t);
} catch (Type.CouldNotInferTypeException e) {
type_inferencer.defer(t);
//e.log(attr.log, t.pos(), t);
//t.type = attr.syms.botType;
} catch (Type.TypeMismatchException e) {
e.log(attr.log, t.pos());
t.type = attr.syms.botType;
}
}
public void
assertInferred(PQLExpr that)
{
for (JCVariableDecl decl : that.decls) {
try {
decl.sym.type = decl.sym.type.elimQTyVars();
if (decl.sym.type.tag == TypeTags.QTYVAR)
throw new RuntimeException();
} catch (Exception _) {
decl.sym.type = decl.sym.type.unifyWith(attr.syms.objectType, attr.types).elimQTyVars();
if (decl.sym.type.tag == TypeTags.QTYVAR)
throw new Type.CouldNotInferTypeException(decl.sym.type);
}
}
}
public void
solveTypes(PQLExpr expr)
{
this.type_inferencer.solve();
assertInferred(expr);
for (PQLExpr e : this.info.inner_pq)
assertInferred(e);
}
class PQLTypeInferencer extends JCTree.Visitor
{
// Nonstandard: for invocations, we have upper bounds on types that we need to enforce after type inference is complete.
LinkedList<JCMethodInvocation> invocations_type_check = new LinkedList<JCMethodInvocation>();
// standard worklist
LinkedList<JCTree> worklist = new LinkedList<JCTree>();
public void
infer(JCTree t)
{
t.accept(this);
}
/**
* Resolve later
*/
public void
defer(JCTree t)
{
t.type = tyvar();
worklist.add(t);
}
public void
solve()
{
int start_count;
// compute lfp
do {
final LinkedList<JCTree> wl = this.worklist;
start_count = wl.size();
this.worklist = new LinkedList<JCTree>();
for (JCTree t : wl) {
final Type ty = t.type;
infer(t);
t.type = ty.unifyWith(t.type, attr.types);
}
if (start_count >= this.worklist.size()) {
for (JCTree t : this.worklist)
throw new RuntimeException("Cannot infer type for " + t);
}
} while (start_count > 0);
for (JCMethodInvocation invoc : this.invocations_type_check)
checkMethodInvocation(invoc);
}
public void
checkMethodInvocation(final JCMethodInvocation invocation)
{
PQLMagicSubexpr checker =
new PQLMagicSubexpr() {
@Override
public void
handleContains(JCExpression set, JCExpression element)
{
set.type = boxRecursively(set.type.elimQTyVars());
element.type = element.type.elimQTyVars();
//set.type.unifyWith(collectionTypeOf(element.type), attr.types);
if (!attr.types.isSubtype(set.type, collectionTypeOf(element.type))) {
attr.log.error(invocation.pos(), "prob.found.req", JCDiagnostic.fragment("incompatible.types"), set.type, collectionTypeOf(element.type));
}
}
@Override
public void
handleMapGet(JCExpression map, JCExpression key)
{
map.type = boxRecursively(map.type.elimQTyVars());
key.type = key.type.elimQTyVars();
invocation.type = invocation.type.elimQTyVars();
final Type expected_type = mapTypeOf(key.type, invocation.type);
//expected_type.unifyWith(map.type, attr.types);
if (!attr.types.isSubtype(map.type, expected_type)) {
attr.log.error(invocation.pos(), "prob.found.req", JCDiagnostic.fragment("incompatible.types"),
map.type, expected_type);
}
}
@Override
public void
handleSize(JCExpression container)
{
container.type = container.type.elimQTyVars();
}
@Override
public void
handleRange(JCExpression first, JCExpression last)
{
}
@Override
public void
handleRangeContains(JCExpression first, JCExpression last, JCExpression body)
{
}
};
checker.process(invocation);
}
public Type
tyvar()
{
return new QInferredType(null);
}
// ================================================================================
public Symbol.MethodSymbol
checkResolvedReductor(PQLExpr owner, JCExpression reductor, Type reduction_type)
{
System.err.println("Reduction type = " + reduction_type);
Env<AttrContext> localEnv = attr.env.dup(owner, attr.env.info.dup());
// first, attribute method
final Type mpt = attr.newMethTemplate(List.of(reduction_type, reduction_type), List.<Type>nil());
attr.env.info.varArgs = false;
Type mtype = attr.attribExpr(reductor, localEnv, mpt);
// this will trigger a type error if there is no matching/visible method.
System.err.println("reductor : " + reductor.getClass());
// while (reductor.getTag() == JCTree.SELECT)
// reductor = ((JCFieldAccess) reductor).selected;
// System.err.println("post-reductor = " + reductor);
// assert reductor.getTag() == JCTree.IDENT;
// final JCIdent ident = (JCIdent) reductor;
final Symbol.MethodSymbol sym;
if (reductor.getTag() == JCTree.IDENT)
sym = (Symbol.MethodSymbol) ((JCIdent) reductor).sym;
else
sym = (Symbol.MethodSymbol) ((JCFieldAccess) reductor).sym;
if (sym == null)
return null;
// Final checks:
// method static?
if ((sym.flags_field & Flags.STATIC) == 0)
attr.log.error(reductor.pos, "reductors.must.be.static", sym);
// method return type matches?
Type.MethodType mty = (Type.MethodType) sym.type;
mty.restype.unifyWith(reduction_type, attr.types);
// all types match suitably?
if (!attr.types.isSameType(mty.argtypes.tail.head.elimQTyVars(), mty.argtypes.head.elimQTyVars()))
attr.log.error(reductor.pos, "reductors.must.have.matching.argtypes", sym);
if (!attr.types.isSameType(mty.restype.elimQTyVars(), (mty.argtypes.head)))
attr.log.error(reductor.pos, "reductors.must.have.matching.result", sym);
return sym;
}
public void visitPQLExpr(PQLExpr that)
{
switch (that.query_kind) {
case EXISTENTIAL:
case UNIVERSAL:
that.type = attr.syms.booleanType;
break;
case SET: {
Type elemtype = PQLBody.normalisedType(attr.syms, attr.types, that.decls.head.sym.type);
that.type = setTypeOf(elemtype);
break;
}
case DEFAULT_MAP:
case MAP: {
Type keytype = PQLBody.normalisedType(attr.syms, attr.types, that.decls.head.sym.type);
Type valuetype = PQLBody.normalisedType(attr.syms, attr.types, that.decls.tail.head.sym.type);
that.type = javaUtilMapTypeOf(keytype, valuetype);
break;
}
case REDUCTION:
System.err.println("Reduction subresult = " + that.decls.head.sym.type);
that.type = that.decls.head.sym.type;//PQLBody.normalisedType(attr.syms, attr.types, that.decls.head.sym.type);
that.resolved_reductor = checkResolvedReductor(that, that.reductor, that.type);
that.type = PQLBody.normalisedType(attr.syms, attr.types, that.type);
break;
default:
throw new RuntimeException("Unsupported PQL query kind: " + that.query_kind);
}
}
public void visitIndexed(JCArrayAccess that)
{
Type ret_ty = tyvar();
that.indexed.type = that.indexed.type.unifyWith(mapTypeOf(that.index.type, ret_ty), attr.types);
//Make no assumptions aobut the index type-- we allow this notation to be used for maps
//that.index.type = that.index.type.unifyWith(attr.syms.intType, attr.types);
that.type = ret_ty.unifyWith(ret_ty, attr.types);
}
public void visitUnary(JCUnary that)
{
final Type inner = that.arg.type;
switch (that.getTag()) {
case JCTree.POS:
case JCTree.NEG:
case JCTree.COMPL:
that.type = inner;
break;
case JCTree.NOT:
that.type = attr.syms.booleanType;
break;
default:
throw new RuntimeException("Unsupported unary expression: " + that);
}
}
public void visitBinary(JCBinary that)
{
final Type left = that.lhs.type;
final Type right = that.rhs.type;
Type result;
// parameters
if (that.getTag() != JCTree.TYPETEST) {
left.unifyWith(right, attr.types);
}
// return type
switch (that.getTag()) {
case JCTree.EQ:
case JCTree.NE:
case JCTree.LT:
case JCTree.GT:
case JCTree.LE:
case JCTree.GE:
case JCTree.OR:
case JCTree.AND:
case JCTree.IMPLIES:
result = attr.syms.booleanType;
break;
default:
result = left;
if (result.tag == TypeTags.BYTE
|| result.tag == TypeTags.SHORT)
result = attr.syms.intType;
}
that.type = result;
}
public void visitParens(JCParens that)
{
that.type = that.expr.type;
}
public void visitAssign(JCAssign that)
{
that.lhs.type = that.lhs.type.unifyWith(that.rhs.type, attr.types);
that.type = attr.syms.booleanType;
}
public Type visitSize(JCExpression container)
{
container.type = container.type.unifyWith(new Type.MagicSizeType(), attr.types);
return attr.syms.intType;
}
public void visitSelect(JCFieldAccess that)
{
if (that.name.toString().equals("length")) {
that.type = visitSize(that.selected);
return;
}
// has to be an object field access, otherwise it would be a logical constant.
// Or a method call, but those are not subject to type inference (either errors or treated specially, cf. JCApply).
final Type inner_type = that.selected.type = that.selected.type.elimQTyVars();
if (inner_type.tag == TypeTags.QTYVAR) {
// Don't know the type yet, so we can't infer or check the field type.
defer(that);
} else {
if (inner_type.tag == TypeTags.BOT) {
that.type = attr.syms.botType;
return;
}
if (inner_type.tag != TypeTags.CLASS) {
attr.log.error(that.pos(), "primitive.type.field", inner_type);
that.type = attr.syms.botType;
return;
}
ClassType ct = (ClassType) inner_type;
// resolve field
final Name name = that.name;
Type site = ct;
if (name == attr.names._class) { // x.class
Type t = attr.syms.classType;
List<Type> typeargs = attr.allowGenerics
? List.of(attr.types.erasure(site))
: List.<Type>nil();
t = new ClassType(t.getEnclosingType(), typeargs, t.tsym);
VarSymbol sym = new VarSymbol(Flags.STATIC | Flags.PUBLIC | Flags.FINAL, attr.names._class, t, site.tsym);
that.sym = sym;
that.type = t;
} else { // x.f
Symbol sym = attr.rs.findIdentInType(attr.env, site, name, Kinds.VAL);
sym = attr.rs.access(sym, that.pos(), site, name, true);
that.sym = sym;
that.type = sym.type;
}
}
}
public void visitApply(final JCMethodInvocation that)
{
class PQLMagicTypeInference extends PQLMagicSubexpr
{
public Type type;
@Override
public void
handleSize(JCExpression container)
{
type = visitSize(container);
}
@Override
public void
handleContains(JCExpression set, JCExpression element)
{
set.type = set.type.unifyWith(collectionTypeOf(element.type), attr.types);
type = attr.syms.booleanType;
}
@Override
public void
handleMapGet(JCExpression map, JCExpression key)
{
Type result_tyvar = new QInferredType(null);
map.type = map.type.unifyWith(mapTypeOf(key.type, result_tyvar), attr.types);
type = result_tyvar;
}
@Override
public void
handleRange(JCExpression first, JCExpression last)
{
first.type = first.type.unifyWith(attr.syms.intType, attr.types);
last.type = last.type.unifyWith(attr.syms.intType, attr.types);
type = collectionTypeOf(attr.syms.intType);
}
@Override
public void
handleRangeContains(JCExpression first, JCExpression last, JCExpression body)
{
first.type = first.type.unifyWith(attr.syms.intType, attr.types);
last.type = last.type.unifyWith(attr.syms.intType, attr.types);
body.type = body.type.unifyWith(attr.syms.intType, attr.types);
type = attr.syms.booleanType;
}
};
PQLMagicTypeInference inferencer = new PQLMagicTypeInference();
if (inferencer.process(that)) {
that.type = inferencer.type;
assert that.type != null;
invocations_type_check.add(that);
} else
visitTree(that); // not a magic expression? -> generic error
}
public void visitConditional(JCConditional that)
{
that.type = that.truepart.type.unifyWith(that.falsepart.type, attr.types);
that.cond.type = that.cond.type.unifyWith(attr.syms.booleanType, attr.types);
}
public void visitTypeTest(JCInstanceOf that)
{
that.type = attr.syms.booleanType;
}
// ----------------------------------------
// FIXME:
public void visitTypeCast(JCTypeCast that) { err(that); }
// ----------------------------------------
// the following should never occur because type inference isn't needed for them:
public void visitLiteral(JCLiteral that)
{
attr.pt = Type.noType;
that.accept(attr);
//throw new RuntimeException("Type inference for JCLiteral shouldn't be needed: " + that);
}
public void visitIdent(JCIdent that)
{
throw new RuntimeException("Type inference for JCIdent shouldn't be needed!");
}
// ----------------------------------------
// disallowed for now:
public void visitIf(JCIf that) { err(that); }
public void visitNewClass(JCNewClass that) { err(that); }
public void visitNewArray(JCNewArray that) { err(that); }
// ----------------------------------------
// the following will only arise if type variables occur in disallowed contexts:
public void visitTypeIdent(JCPrimitiveTypeTree that) { visitTree(that); }
public void visitTypeArray(JCArrayTypeTree that) { visitTree(that); }
public void visitTypeApply(JCTypeApply that) { visitTree(that); }
public void visitTypeParameter(JCTypeParameter that) { visitTree(that); }
public void visitWildcard(JCWildcard that) { visitTree(that); }
public void visitTypeBoundKind(TypeBoundKind that) { visitTree(that); }
public void visitAnnotation(JCAnnotation that) { visitTree(that); }
public void visitModifiers(JCModifiers that) { visitTree(that); }
public void visitReturn(JCReturn that) { visitTree(that); }
public void visitAssert(JCAssert that) { visitTree(that); }
public void visitExec(JCExpressionStatement that) { visitTree(that); }
public void visitBreak(JCBreak that) { visitTree(that); }
public void visitContinue(JCContinue that) { visitTree(that); }
public void visitThrow(JCThrow that) { visitTree(that); }
public void visitAssignop(JCAssignOp that) { visitTree(that); }
public void visitErroneous(JCErroneous that) { }
public void visitClassDef(JCClassDecl that) { visitTree(that); }
public void visitSkip(JCSkip that) { visitTree(that); }
public void visitBlock(JCBlock that) { visitTree(that); }
public void visitLabelled(JCLabeledStatement that) { visitTree(that); }
public void visitSwitch(JCSwitch that) { visitTree(that); }
public void visitCase(JCCase that) { visitTree(that); }
public void visitSynchronized(JCSynchronized that) { visitTree(that); }
public void visitTry(JCTry that) { visitTree(that); }
public void visitCatch(JCCatch that) { visitTree(that); }
public void visitTree(JCTree that) {}
public void visitTopLevel(JCCompilationUnit that) { err(that); }
public void visitImport(JCImport that) { err(that); }
public void visitMethodDef(JCMethodDecl that) { err(that); }
public void visitVarDef(JCVariableDecl that) { err(that); }
public void visitDoLoop(JCDoWhileLoop that) { err(that); }
public void visitWhileLoop(JCWhileLoop that) { err(that); }
public void visitForLoop(JCForLoop that) { err(that); }
public void visitForeachLoop(JCEnhancedForLoop that) { err(that); }
public void visitLetExpr(LetExpr that) { err(that); }
public void err(JCTree that)
{
that.type = attr.syms.botType;
}
}
}