/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * 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. * * 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. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.comp; import org.visage.api.VisageBindStatus; import java.util.HashMap; import java.util.Map; import java.util.ArrayList; import javax.lang.model.element.ElementKind; import javax.tools.JavaFileObject; import org.visage.api.tree.ForExpressionInClauseTree; import org.visage.api.tree.Tree.VisageKind; import org.visage.api.tree.TypeTree.Cardinality; import com.sun.tools.mjavac.code.*; import static com.sun.tools.mjavac.code.Flags.*; import static com.sun.tools.mjavac.code.Flags.ANNOTATION; import static com.sun.tools.mjavac.code.Flags.BLOCK; import static com.sun.tools.mjavac.code.Kinds.*; import static com.sun.tools.mjavac.code.Kinds.ERRONEOUS; import com.sun.tools.mjavac.code.Symbol.*; import com.sun.tools.mjavac.code.Type.*; import static com.sun.tools.mjavac.code.TypeTags.*; import static com.sun.tools.mjavac.code.TypeTags.WILDCARD; import com.sun.tools.mjavac.comp.*; import com.sun.tools.mjavac.jvm.ByteCodes; import com.sun.tools.mjavac.jvm.Target; import com.sun.tools.mjavac.util.*; import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition; import org.visage.tools.code.*; import org.visage.tools.tree.*; import org.visage.tools.util.MsgSym; import static org.visage.tools.code.VisageFlags.SCRIPT_LEVEL_SYNTH_STATIC; import org.visage.tools.comp.VisageCheck.WriteKind; /** This is the main context-dependent analysis phase in GJC. It * encompasses name resolution, type checking and constant folding as * subtasks. Some subtasks involve auxiliary classes. * @see Check * @see Resolve * @see ConstFold * @see Infer * * This class is interleaved with {@link VisageMemberEnter}, which is used * to enter declarations into a local scope. * * <p><b>This is NOT part of any API supported by Sun Microsystems. If * you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class VisageAttr implements VisageVisitor { protected static final Context.Key<VisageAttr> visageAttrKey = new Context.Key<VisageAttr>(); /* * modules imported by context */ private final VisageDefs defs; private final Name.Table names; private final Log log; private final VisageResolve rs; private final VisageSymtab syms; private final VisageCheck chk; private final Messages messages; private final VisageMemberEnter memberEnter; private final JCDiagnostic.Factory diags; private final VisageTreeMaker visagemake; private final ConstFold cfolder; private final VisageEnter enter; private final Target target; private final VisageTypes types; private final Annotate annotate; /* * other instance information */ private final Source source; Map<VisageVarSymbol, VisageVar> varSymToTree = new HashMap<VisageVarSymbol, VisageVar>(); Map<MethodSymbol, VisageFunctionDefinition> methodSymToTree = new HashMap<MethodSymbol, VisageFunctionDefinition>(); public static VisageAttr instance(Context context) { VisageAttr instance = context.get(visageAttrKey); if (instance == null) instance = new VisageAttr(context); return instance; } protected VisageAttr(Context context) { context.put(visageAttrKey, this); defs = VisageDefs.instance(context); syms = (VisageSymtab)VisageSymtab.instance(context); names = Name.Table.instance(context); log = Log.instance(context); diags = JCDiagnostic.Factory.instance(context); messages = Messages.instance(context); rs = VisageResolve.instance(context); chk = VisageCheck.instance(context); memberEnter = VisageMemberEnter.instance(context); visagemake = (VisageTreeMaker)VisageTreeMaker.instance(context); enter = VisageEnter.instance(context); cfolder = ConstFold.instance(context); target = Target.instance(context); types = VisageTypes.instance(context); annotate = Annotate.instance(context); Options options = Options.instance(context); source = Source.instance(context); allowGenerics = source.allowGenerics(); allowVarargs = source.allowVarargs(); allowBoxing = source.allowBoxing(); allowCovariantReturns = source.allowCovariantReturns(); allowAnonOuterThis = source.allowAnonOuterThis(); relax = (options.get("-retrofit") != null || options.get("-relax") != null); String pkgs = options.get("warnOnUse"); if (pkgs != null) { warnOnUsePackages = pkgs.split(","); } } /** Switch: relax some constraints for retrofit mode. */ private boolean relax; /** Switch: support generics? */ private boolean allowGenerics; /** Switch: allow variable-arity methods. */ private boolean allowVarargs; /** Switch: support boxing and unboxing? */ private boolean allowBoxing; /** Switch: support covariant result types? */ private boolean allowCovariantReturns; /** Switch: allow references to surrounding object from anonymous * objects during constructor call? */ private boolean allowAnonOuterThis; /** * Packages for which we have to issue warnings. */ private String[] warnOnUsePackages; enum Sequenceness { DISALLOWED, PERMITTED, REQUIRED } /** Check kind and type of given tree against protokind and prototype. * If check succeeds, store type in tree and return it. * If check fails, store errType in tree and return it. * No checks are performed if the prototype is a method type. * Its not necessary in this case since we know that kind and type * are correct. WRONG - see VSGC-2199. * * @param tree The tree whose kind and type is checked * @param owntype The computed type of the tree * @param ownkind The computed kind of the tree * @param pkind The expected kind (or: protokind) of the tree * @param pt The expected type (or: prototype) of the tree */ Type check(VisageTree tree, Type owntype, int ownkind, int pkind, Type pt, Sequenceness pSequenceness) { return check(tree, owntype, ownkind, pkind, pt, pSequenceness, true); } Type check(VisageTree tree, Type owntype, int ownkind, int pkind, Type pt, Sequenceness pSequenceness, boolean giveWarnings) { if (owntype != null && owntype != syms.visage_UnspecifiedType && owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) { // if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) { if ((pkind & VAL) != 0 && ownkind == MTH) { ownkind = VAL; if (owntype instanceof MethodType) { owntype = chk.checkFunctionType(tree.pos(), (MethodType)owntype); } } if ((ownkind & ~pkind) == 0) { owntype = chk.checkType(tree.pos(), owntype, pt, pSequenceness, giveWarnings); } else { log.error(tree.pos(), MsgSym.MESSAGE_UNEXPECTED_TYPE, Resolve.kindNames(pkind), Resolve.kindName(ownkind)); owntype = syms.errType; } } tree.type = owntype; return owntype; } /** Is this symbol a type? */ static boolean isType(Symbol sym) { return sym != null && sym.kind == TYP; } /** The current `this' symbol. * @param env The current environment. */ Symbol thisSym(DiagnosticPosition pos, VisageEnv<VisageAttrContext> env) { return rs.resolveSelf(pos, env, env.enclClass.sym, names._this); } /* ************************************************************************ * Visitor methods *************************************************************************/ /** Visitor argument: the current environment. */ private VisageEnv<VisageAttrContext> env; /** Visitor argument: the currently expected proto-kind. */ int pkind; /** Visitor argument: the currently expected proto-type. */ Type pt; /** Visitor argument: is a sequence permitted */ private Sequenceness pSequenceness; /** Visitor result: the computed type. */ private Type result; /** Visitor method: attribute a tree, catching any completion failure * exceptions. Return the tree's type. * * @param tree The tree to be visited. * @param env The environment visitor argument. * @param pkind The protokind visitor argument. * @param pt The prototype visitor argument. */ Type attribTree(VisageTree tree, VisageEnv<VisageAttrContext> env, int pkind, Type pt) { return attribTree(tree, env, pkind, pt, this.pSequenceness); } Type attribTree(VisageTree tree, VisageEnv<VisageAttrContext> env, int pkind, Type pt, Sequenceness pSequenceness) { VisageEnv<VisageAttrContext> prevEnv = this.env; int prevPkind = this.pkind; Type prevPt = this.pt; Sequenceness prevSequenceness = this.pSequenceness; try { this.env = env; this.pkind = pkind; this.pt = pt; this.pSequenceness = pSequenceness; if (tree != null )tree.accept(this); if (tree == breakTree) throw new BreakAttr(env); if (pSequenceness == Sequenceness.REQUIRED && result.tag != ERROR && ! types.isSequence(result)) { result = chk.typeTagError(tree, types.sequenceType(syms.unknownType), result); } return result; } catch (CompletionFailure ex) { tree.type = syms.errType; return chk.completionError(tree.pos(), ex); } finally { this.env = prevEnv; this.pkind = prevPkind; this.pt = prevPt; this.pSequenceness = prevSequenceness; } } /** Derived visitor method: attribute an expression tree. */ Type attribExpr(VisageTree tree, VisageEnv<VisageAttrContext> env, Type pt, Sequenceness pSequenceness) { return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, pt.tag != ERROR ? pSequenceness : Sequenceness.PERMITTED); } /** Derived visitor method: attribute an expression tree. * allow a sequence if no proto-type is specified, the proto-type is a seqeunce, * or the proto-type is an error. */ Type attribExpr(VisageTree tree, VisageEnv<VisageAttrContext> env, Type pt) { return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, (pt.tag == ERROR || pt == Type.noType || types.isSequence(pt))? Sequenceness.PERMITTED : Sequenceness.DISALLOWED); } /** Derived visitor method: attribute an expression tree with * no constraints on the computed type. */ Type attribExpr(VisageTree tree, VisageEnv<VisageAttrContext> env) { return attribTree(tree, env, VAL, Type.noType, Sequenceness.PERMITTED); } /** Derived visitor method: attribute a type tree. */ Type attribType(VisageTree tree, VisageEnv<VisageAttrContext> env) { Type localResult = attribTree(tree, env, TYP, Type.noType, Sequenceness.PERMITTED); return localResult; } /** Derived visitor method: attribute a statement or definition tree. */ public Type attribDecl(VisageTree tree, VisageEnv<VisageAttrContext> env) { return attribTree(tree, env, NIL, Type.noType, Sequenceness.DISALLOWED); } public Type attribVar(VisageVar tree, VisageEnv<VisageAttrContext> env) { memberEnter.memberEnter(tree, env); return attribExpr(tree, env); } /** Attribute a list of expressions, returning a list of types. */ List<Type> attribExprs(List<VisageExpression> trees, VisageEnv<VisageAttrContext> env, Type pt) { ListBuffer<Type> ts = new ListBuffer<Type>(); for (List<VisageExpression> l = trees; l.nonEmpty(); l = l.tail) ts.append(attribExpr(l.head, env, pt)); return ts.toList(); } /** Attribute the arguments in a method call, returning a list of types. */ List<Type> attribArgs(List<VisageExpression> trees, VisageEnv<VisageAttrContext> env) { ListBuffer<Type> argtypes = new ListBuffer<Type>(); for (List<VisageExpression> l = trees; l.nonEmpty(); l = l.tail) argtypes.append(chk.checkNonVoid( l.head.pos(), types.upperBound(attribTree(l.head, env, VAL, Infer.anyPoly)))); return argtypes.toList(); } /** Does tree represent a static reference to an identifier? * It is assumed that tree is either a SELECT or an IDENT. * We have to weed out selects from non-type names here. * @param tree The candidate tree. */ boolean isStaticReference(VisageTree tree) { if (tree.getVisageTag() == VisageTag.SELECT) { Symbol lsym = VisageTreeInfo.symbol(((VisageSelect) tree).selected); if (lsym == null || lsym.kind != TYP) { return false; } } return true; } /** Attribute a list of statements, returning nothing. */ <T extends VisageTree> void attribDecls(List<T> trees, VisageEnv<VisageAttrContext> env) { for (List<T> l = trees; l.nonEmpty(); l = l.tail) attribDecl(l.head, env); } /** Attribute a type argument list, returning a list of types. */ List<Type> attribTypes(List<VisageExpression> trees, VisageEnv<VisageAttrContext> env) { ListBuffer<Type> argtypes = new ListBuffer<Type>(); for (List<VisageExpression> l = trees; l.nonEmpty(); l = l.tail) argtypes.append(chk.checkRefType(l.head.pos(), attribType(l.head, env))); return argtypes.toList(); } /** Attribute type reference in an `extends' or `implements' clause. * * @param tree The tree making up the type reference. * @param env The environment current at the reference. * @param classExpected true if only a class is expected here. * @param interfaceExpected true if only an interface is expected here. */ Type attribBase(VisageTree tree, VisageEnv<VisageAttrContext> env, boolean classExpected, boolean interfaceExpected, boolean checkExtensible) { Type t = attribType(tree, env); return checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible); } Type checkBase(Type t, VisageTree tree, VisageEnv<VisageAttrContext> env, boolean classExpected, boolean interfaceExpected, boolean checkExtensible) { if (t.tag == TYPEVAR && !classExpected && !interfaceExpected) { // check that type variable is already visible if (t.getUpperBound() == null) { log.error(tree.pos(), MsgSym.MESSAGE_ILLEGAL_FORWARD_REF); return syms.errType; } } else { t = chk.checkClassType(tree.pos(), t, checkExtensible|!allowGenerics); } if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) { log.error(tree.pos(), MsgSym.MESSAGE_INTF_EXPECTED_HERE); // return errType is necessary since otherwise there might // be undetected cycles which cause attribution to loop return syms.errType; } else if (checkExtensible && classExpected && (t.tsym.flags() & INTERFACE) != 0) { log.error(tree.pos(), MsgSym.MESSAGE_NO_INTF_EXPECTED_HERE); return syms.errType; } if (checkExtensible && ((t.tsym.flags() & FINAL) != 0)) { log.error(tree.pos(), MsgSym.MESSAGE_CANNOT_INHERIT_FROM_FINAL, t.tsym); } chk.checkNonCyclic(tree.pos(), t); return t; } private VisageEnv<VisageAttrContext> newLocalEnv(VisageTree tree) { VisageEnv<VisageAttrContext> localEnv = env.dup(tree, env.info.dup(env.info.scope.dupUnshared())); localEnv.outer = env; localEnv.info.scope.owner = new MethodSymbol(BLOCK, names.empty, null, env.enclClass.sym); return localEnv; } //@Override public void visitTypeCast(VisageTypeCast tree) { Type clazztype = attribType(tree.clazz, env); Type exprtype = attribExpr(tree.expr, env); Type owntype = chk.checkCastable(tree.expr.pos(), exprtype, clazztype); if (exprtype.constValue() != null) owntype = cfolder.coerce(exprtype, owntype); result = check(tree, capture(owntype), VAL, pkind, pt, Sequenceness.DISALLOWED); } //@Override public void visitInstanceOf(VisageInstanceOf tree) { Type exprtype = attribExpr(tree.expr, env); Type type = attribType(tree.clazz, env); //FIXME - check that the target type is not a generic type - this hack //disables instanceof where target type is a sequence, currently //not supported by translation result = chk.checkReifiableReferenceType( tree.clazz.pos(), types.boxedTypeOrType(type)); if (!result.isErroneous()) { chk.checkInstanceOf(tree.expr.pos(), exprtype, type); result = check(tree, syms.booleanType, VAL, pkind, pt, Sequenceness.DISALLOWED); } } private void checkTypeCycle(VisageTree tree, Symbol sym) { if (sym.type == null) { VisageVar var = varSymToTree.get(sym); if (var != null) { JavaFileObject prevSource = log.currentSource(); try { //we need to switch log source as the var def could be //in another source w.r.t. the current one log.useSource(sym.outermostClass().sourcefile); log.note(var, MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_VAR_DECL, sym.name); } finally { log.useSource(prevSource); } } log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_VAR_REF, sym.name); sym.type = syms.errType; } else if (sym.type instanceof MethodType && sym.type.getReturnType() == syms.unknownType) { VisageFunctionDefinition fun = methodSymToTree.get(sym); if (fun != null) { JavaFileObject prevSource = log.currentSource(); try { //we need to switch log source as the func def could be //in another source w.r.t. the current one log.useSource(sym.outermostClass().sourcefile); log.note(fun, MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_FUN_DECL, sym.name); } finally { log.useSource(prevSource); } } log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_VAR_REF, sym.name); if (pt instanceof MethodType) ((MethodType)pt).restype = syms.errType; sym.type = syms.errType; } } //@Override public void visitIdent(VisageIdent tree) { Symbol sym; boolean varArgs = false; // Find symbol if (tree.sym != null && tree.sym.kind != VAR) { sym = tree.sym; } else { sym = rs.resolveIdent(tree.pos(), env,tree.getName(), pkind, pt); } tree.sym = sym; sym.complete(); checkTypeCycle(tree, sym); // (1) Also find the environment current for the class where // sym is defined (`symEnv'). // Only for pre-tiger versions (1.4 and earlier): // (2) Also determine whether we access symbol out of an anonymous // class in a this or super call. This is illegal for instance // members since such classes don't carry a this$n link. // (`noOuterThisPath'). VisageEnv<VisageAttrContext> symEnv = env; boolean noOuterThisPath = false; if (env.enclClass.sym.owner.kind != PCK && // we are in an inner class (sym.kind & (VAR | MTH | TYP)) != 0 && sym.owner.kind == TYP && tree.getName() != names._this && tree.getName() != names._super) { // Find environment in which identifier is defined. while (symEnv.outer != null && !sym.isMemberOf(symEnv.enclClass.sym, types)) { if ((symEnv.enclClass.sym.flags() & NOOUTERTHIS) != 0) noOuterThisPath = !allowAnonOuterThis; symEnv = symEnv.outer; } } // In a constructor body, // if symbol is a field or instance method, check that it is // not accessed before the supertype constructor is called. if ((symEnv.info.isSelfCall || noOuterThisPath) && (sym.kind & (VAR | MTH)) != 0 && sym.owner.kind == TYP && (sym.flags() & STATIC) == 0) { chk.earlyRefError(tree.pos(), sym.kind == VAR ? sym : thisSym(tree.pos(), env)); } VisageEnv<VisageAttrContext> env1 = env; if (sym.kind != ERR && sym.owner != null && sym.owner != env1.enclClass.sym) { // If the found symbol is inaccessible, then it is // accessed through an enclosing instance. Locate this // enclosing instance: while (env1.outer != null && !rs.isAccessible(env, env1.enclClass.sym.type, sym)) env1 = env1.outer; } // If symbol is a variable, ... if (sym.kind == VAR) { VisageVarSymbol v = (VisageVarSymbol)sym; if (env.info.inInvalidate && sym == env.info.enclVar) { log.error(tree.pos(), MsgSym.MESSAGE_CANNOT_REF_INVALIDATE_VAR, sym); } // If we are expecting a variable (as opposed to a value), check // that the variable is assignable in the current environment. if (pkind == VAR) chk.checkAssignable(tree.pos(), v, null, env1.enclClass.sym.type, env, WriteKind.ASSIGN); } result = checkId(tree, env1.enclClass.sym.type, sym, env, pkind, pt, pSequenceness, varArgs); } //@Override public void visitSelect(VisageSelect tree) { // Determine the expected kind of the qualifier expression. int skind = 0; if (tree.name == names._this || tree.name == names._super || tree.name == names._class) { skind = TYP; } else { if ((pkind & PCK) != 0) skind = skind | PCK; if ((pkind & TYP) != 0) skind = skind | TYP | PCK; if ((pkind & (VAL | MTH)) != 0) skind = skind | VAL | TYP; } // Attribute the qualifier expression, and determine its symbol (if any). Type site = attribTree(tree.selected, env, skind, Infer.anyPoly, Sequenceness.PERMITTED); boolean wasPrimitive = site.isPrimitive(); site = types.boxedTypeOrType(site); if ((pkind & (PCK | TYP)) == 0) site = capture(site); // Capture field access // don't allow T.class T[].class, etc if (skind == TYP) { Type elt = site; while (elt.tag == ARRAY) elt = ((ArrayType)elt).elemtype; if (elt.tag == TYPEVAR) { log.error(tree.pos(), MsgSym.MESSAGE_TYPE_VAR_CANNOT_BE_DEREF); result = syms.errType; return; } } // If qualifier symbol is a type or `super', assert `selectSuper' // for the selection. This is relevant for determining whether // protected symbols are accessible. Symbol sitesym = VisageTreeInfo.symbol(tree.selected); boolean selectSuperPrev = env.info.selectSuper; env.info.selectSuper = sitesym != null && sitesym.name == names._super; // If selected expression is polymorphic, strip // type parameters and remember in env.info.tvars, so that // they can be added later (in Attr.checkId and Infer.instantiateMethod). if (tree.selected.type.tag == FORALL) { ForAll pstype = (ForAll)tree.selected.type; env.info.tvars = pstype.tvars; site = tree.selected.type = pstype.qtype; } // Determine the symbol represented by the selection. env.info.varArgs = false; if (sitesym instanceof ClassSymbol && (types.isSameType(sitesym.type, syms.objectType) || env.enclClass.sym.isSubClass(sitesym, types))) env.info.selectSuper = true; Symbol sym = selectSym(tree, site, env, pt, pkind); sym.complete(); if (sym.exists() && !isType(sym) && (pkind & (PCK | TYP)) != 0) { site = capture(site); sym = selectSym(tree, site, env, pt, pkind); } boolean varArgs = env.info.varArgs; tree.sym = sym; if (wasPrimitive && sym.isStatic() && tree.selected instanceof VisageIdent) tree.selected.type = site; checkTypeCycle(tree, sym); if (site.tag == TYPEVAR && !isType(sym) && sym.kind != ERR) site = capture(site.getUpperBound()); // If that symbol is a variable, ... if (sym.kind == VAR) { VisageVarSymbol v = (VisageVarSymbol)sym; // If we are expecting a variable (as opposed to a value), check // that the variable is assignable in the current environment. if (pkind == VAR) chk.checkAssignable(tree.pos(), v, tree.selected, site, env, WriteKind.ASSIGN); } // Disallow selecting a type from an expression if (isType(sym) && (sitesym==null || (sitesym.kind&(TYP|PCK)) == 0)) { tree.type = check(tree.selected, pt, sitesym == null ? VAL : sitesym.kind, TYP|PCK, pt, pSequenceness); } if (isType(sitesym)) { if (sym.name == names._this) { // If `C' is the currently compiled class, check that // C.this' does not appear in a call to a super(...) if (env.info.isSelfCall && site.tsym == env.enclClass.sym) { chk.earlyRefError(tree.pos(), sym); } } else if (! sym.isStatic()) { for (VisageEnv<VisageAttrContext> env1 = env; ; env1 = env1.outer) { if (env1 == null) { rs.access(rs.new StaticError(sym), tree.pos(), site, sym.name, true); break; } if (env1.tree instanceof VisageFunctionDefinition && ((VisageFunctionDefinition)env1.tree).isStatic()) { rs.access(rs.new StaticError(sym), tree.pos(), site, sym.name, true); break; } if (env1.tree instanceof VisageClassDeclaration && types.isSubtype(((VisageClassDeclaration) env1.tree).sym.type, site)) { break; } } } } // If we are selecting an instance member via a `super', ... if (env.info.selectSuper && (sym.flags() & STATIC) == 0) { // Check that super-qualified symbols are not abstract (JLS) rs.checkNonAbstract(tree.pos(), sym); if (env.enclClass.sym instanceof VisageClassSymbol) { // Check that the selectet type is a direct supertype of the enclosing class chk.checkSuper(tree.pos(), (VisageClassSymbol)env.enclClass.sym, site); } if (site.isRaw()) { // Determine argument types for site. Type site1 = types.asSuper(env.enclClass.sym.type, site.tsym); if (site1 != null) site = site1; } } env.info.selectSuper = selectSuperPrev; result = checkId(tree, site, sym, env, pkind, pt, pSequenceness, varArgs); if (tree.sym.kind == MTH && result instanceof FunctionType) tree.sym = new MethodSymbol(sym.flags_field, sym.name, ((FunctionType)result).mtype, sym.owner); env.info.tvars = List.nil(); } //where /** Determine symbol referenced by a Select expression, * * @param tree The select tree. * @param site The type of the selected expression, * @param env The current environment. * @param pt The current prototype. * @param pkind The expected kind(s) of the Select expression. */ @SuppressWarnings("fallthrough") private Symbol selectSym(VisageSelect tree, Type site, VisageEnv<VisageAttrContext> env, Type pt, int pkind) { DiagnosticPosition pos = tree.pos(); Name name = tree.name; Symbol sym; switch (site.tag) { case PACKAGE: return rs.access( rs.findIdentInPackage(env, site.tsym, name, pkind), pos, site, name, true); case ARRAY: case CLASS: if (pt.tag == METHOD || pt.tag == FORALL) { return rs.resolveQualifiedMethod(pos, env, site, name, pt); } else if (name == names._this || name == names._super) { return rs.resolveSelf(pos, env, site.tsym, name); } else if (name == names._class) { // In this case, we have already made sure in // visitSelect that qualifier expression is a type. Type t = syms.classType; List<Type> typeargs = allowGenerics ? List.of(types.erasure(site)) : List.<Type>nil(); t = new ClassType(t.getEnclosingType(), typeargs, t.tsym); return new VisageVarSymbol( types, names,STATIC | PUBLIC | FINAL | VisageFlags.VARUSE_SPECIAL, names._class, t, site.tsym); } else { // We are seeing a plain identifier as selector. sym = rs.findIdentInType(env, site, name, pkind); if ((pkind & ERRONEOUS) == 0) sym = rs.access(sym, pos, site, name, true); return sym; } case WILDCARD: throw new AssertionError(tree); case TYPEVAR: // Normally, site.getUpperBound() shouldn't be null. // It should only happen during memberEnter/attribBase // when determining the super type which *must* be // done before attributing the type variables. In // other words, we are seeing this illegal program: // class B<T> extends A<T.foo> {} sym = (site.getUpperBound() != null) ? selectSym(tree, capture(site.getUpperBound()), env, pt, pkind) : null; if (sym == null || isType(sym)) { log.error(pos, MsgSym.MESSAGE_TYPE_VAR_CANNOT_BE_DEREF); return syms.errSymbol; } else { return sym; } case ERROR: // preserve identifier names through errors return new ErrorType(name, site.tsym).tsym; default: // The qualifier expression is of a primitive type -- only // .class is allowed for these. if (name == names._class) { // In this case, we have already made sure in Select that // qualifier expression is a type. Type t = syms.classType; Type arg = types.boxedTypeOrType(site); t = new ClassType(t.getEnclosingType(), List.of(arg), t.tsym); return new VisageVarSymbol( types, names,STATIC | PUBLIC | FINAL | VisageFlags.VARUSE_SPECIAL, names._class, t, site.tsym); } else { log.error(pos, MsgSym.MESSAGE_CANNOT_DEREF, site); return syms.errSymbol; } } } //@Override public void visitParens(VisageParens tree) { Type owntype = attribTree(tree.expr, env, pkind, pt); result = check(tree, owntype, pkind, pkind, pt, pSequenceness); Symbol sym = VisageTreeInfo.symbol(tree); if (sym != null && (sym.kind&(TYP|PCK)) != 0) log.error(tree.pos(), MsgSym.MESSAGE_ILLEGAL_START_OF_TYPE); } //@Override public void visitAssign(VisageAssign tree) { Type owntype = null; VisageEnv<VisageAttrContext> dupEnv = env.dup(tree); dupEnv.outer = env; owntype = attribTree(tree.lhs, dupEnv, VAR, Type.noType); boolean hasLhsType; if (owntype == null || owntype == syms.visage_UnspecifiedType) { owntype = attribExpr(tree.rhs, env, Type.noType); hasLhsType = false; } else { hasLhsType = true; } Symbol lhsSym = VisageTreeInfo.symbol(tree.lhs); if (lhsSym == null) { log.error(tree, MsgSym.MESSAGE_VISAGE_INVALID_ASSIGNMENT); return; } if (hasLhsType) { attribExpr(tree.rhs, dupEnv, owntype); if (types.isSequence(tree.rhs.type) && tree.lhs.getVisageTag() == VisageTag.SEQUENCE_INDEXED) { owntype = types.sequenceType(types.elementTypeOrType(owntype)); } } else { if (tree.lhs.getVisageTag() == VisageTag.SELECT) { VisageSelect fa = (VisageSelect)tree.lhs; fa.type = owntype; } else if (tree.lhs.getVisageTag() == VisageTag.IDENT) { VisageIdent id = (VisageIdent)tree.lhs; id.type = owntype; } attribTree(tree.lhs, dupEnv, VAR, owntype); lhsSym.type = owntype; } result = check(tree, capture(owntype), VAL, pkind, pt, pSequenceness); if (tree.rhs != null && tree.lhs.getVisageTag() == VisageTag.IDENT) { VisageVar lhsVar = varSymToTree.get(lhsSym); if (lhsVar != null && (lhsVar.getVisageType() instanceof VisageTypeUnknown)) { if (lhsVar.type == null || lhsVar.type == syms.visage_AnyType || lhsVar.type == syms.visage_UnspecifiedType) { if (tree.rhs.type != syms.botType && tree.rhs.type != null && lhsVar.type != tree.rhs.type) { tree.type = tree.lhs.type = lhsVar.type = lhsSym.type = types.normalize(tree.rhs.type); lhsVar.setVisageType(visagemake.at(tree.pos()).TypeClass(visagemake.Type(lhsSym.type), lhsVar.getVisageType().getCardinality())); } } } } } public void finishVar(VisageVar tree, VisageEnv<VisageAttrContext> env) { VisageVarSymbol v = tree.sym; // The info.lint field in the envs stored in enter.typeEnvs is deliberately uninitialized, // because the annotations were not available at the time the env was created. Therefore, // we look up the environment chain for the first enclosing environment for which the // lint value is set. Typically, this is the parent env, but might be further if there // are any envs created as a result of TypeParameter nodes. VisageEnv<VisageAttrContext> lintEnv = env; while (lintEnv.info.lint == null) lintEnv = lintEnv.next; Lint lint = lintEnv.info.lint.augment(v.attributes_field, v.flags()); Lint prevLint = chk.setLint(lint); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); try { Type declType = attribType(tree.getVisageType(), env); declType = chk.checkNonVoid(tree.getVisageType(), declType); if (declType != syms.visage_UnspecifiedType) { result = tree.type = v.type = declType; } // Check that the variable's declared type is well-formed. // chk.validate(tree.vartype); Type initType; if (tree.getInitializer() == null && (tree.getModifiers().flags & VisageFlags.IS_DEF) != 0) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_DEF_MUST_HAVE_INIT, v); } if (tree.getInitializer() != null) { if (tree.getInitializer().getVisageKind() == VisageKind.INSTANTIATE_OBJECT_LITERAL && (tree.getModifiers().flags & VisageFlags.IS_DEF) != 0) v.flags_field |= VisageFlags.OBJ_LIT_INIT; // Attribute initializer in a new environment. // Check that initializer conforms to variable's declared type. Scope initScope = new Scope(new MethodSymbol(BLOCK, v.name, null, env.enclClass.sym)); initScope.next = env.info.scope; VisageEnv<VisageAttrContext> initEnv = env.dup(tree, env.info.dup(initScope)); initEnv.outer = env; initEnv.info.lint = lint; if ((tree.getModifiers().flags & STATIC) != 0) initEnv.info.staticLevel++; // In order to catch self-references, we set the variable's // declaration position to maximal possible value, effectively // marking the variable as undefined. initEnv.info.enclVar = v; initType = attribExpr(tree.getInitializer(), initEnv, declType); /* * We introduce a synthetic variable for bound function result. * See VisageBoundContextAnalysis. If the type of that var is * Void, then return statement will catch it and produce appropriate * error message ("Bound function can not be void"). */ if (tree.name != defs.boundFunctionResultName) { initType = chk.checkNonVoid(tree.pos(), initType); } chk.checkType(tree.pos(), initType, declType, types.isSequence(declType) ? Sequenceness.REQUIRED : Sequenceness.DISALLOWED, false); chk.checkBidiBind( tree.getInitializer(),tree.getBindStatus(), initEnv, v.type); } else if (tree.type != null) initType = tree.type; else initType = syms.objectType; // nothing to go on, so we assume Object if (declType == syms.visage_UnspecifiedType && v.type == null) { result = tree.type = v.type = types.normalize(initType); } //chk.validateAnnotations(tree.mods.annotations, v); chk.checkBoundArrayVar(tree); } finally { chk.setLint(prevLint); log.useSource(prev); } } //@Override public void visitVarInit(VisageVarInit tree) { result = tree.type = attribExpr(tree.getVar(), env); } //@Override public void visitVarRef(VisageVarRef tree) { throw new AssertionError("Shouldn't be here!"); } //@Override public void visitVar(VisageVar tree) { long flags = tree.getModifiers().flags; Symbol sym = tree.sym; if (sym == null) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_VAR_NOT_SUPPORTED_HERE, (flags & VisageFlags.IS_DEF) == 0 ? "var" : "def", tree.getName()); return; } sym.complete(); boolean isClassVar = env.info.scope.owner.kind == TYP; if (isClassVar && (flags & STATIC) == 0L) { // Check that instance variables don't override chk.checkVarOverride(tree, (VisageVarSymbol)sym); } //variable decl in bind context with no initializer are not allowed if ((tree.sym.flags_field & Flags.PARAMETER) == 0 && env.tree.getVisageTag() != VisageTag.FOR_EXPRESSION && tree.getInitializer() == null && tree.getBindStatus() != VisageBindStatus.UNBOUND) { log.error(tree.pos(), MsgSym.MESSAGE_TRIGGER_VAR_IN_BIND_MUST_HAVE_INIT, tree.sym); } for (VisageOnReplace.Kind triggerKind : VisageOnReplace.Kind.values()) { VisageOnReplace trigger = tree.getTrigger(triggerKind); boolean inInvalidate = triggerKind == VisageOnReplace.Kind.ONINVALIDATE; if (trigger != null) { if (triggerKind == VisageOnReplace.Kind.ONREPLACE) { VisageVar oldValue = trigger.getOldValue(); if (oldValue != null && oldValue.type == null) { oldValue.type = tree.type; } VisageVar newElements = trigger.getNewElements(); if (newElements != null && newElements.type == null) newElements.type = tree.type; } else if (triggerKind == VisageOnReplace.Kind.ONINVALIDATE) { // if ((sym.flags_field & VisageFlags.VARUSE_BOUND_INIT) == 0) { // log.error(trigger.pos(), MsgSym.MESSAGE_ON_INVALIDATE_UNBOUND_NOT_ALLOWED, sym); // } } if (isClassVar) { // let the owner of the environment be a freshly // created BLOCK-method. VisageEnv<VisageAttrContext> localEnv = newLocalEnv(tree); localEnv.info.inInvalidate = inInvalidate; if (inInvalidate) { localEnv.info.enclVar = sym; } if ((flags & STATIC) != 0) { localEnv.info.staticLevel++; } attribDecl(trigger, localEnv); } else { // Create a new local environment with a local scope. VisageEnv<VisageAttrContext> localEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); localEnv.info.inInvalidate = inInvalidate; if (inInvalidate) { localEnv.info.enclVar = sym; } attribDecl(trigger, localEnv); localEnv.info.scope.leave(); } } } warnOnStaticUse(tree.pos(), tree.getModifiers(), sym); result = /*tree.isBound()? syms.voidType : */ tree.type; } private void warnOnStaticUse(DiagnosticPosition pos, VisageModifiers mods, Symbol sym) { // temporary warning for the use of 'static' if ((mods.flags & (STATIC | SCRIPT_LEVEL_SYNTH_STATIC)) == STATIC) { log.warning(pos, MsgSym.MESSAGE_VISAGE_STATIC_DEPRECATED, sym); } } /** * OK, this is a not really "finish" as in the completer, at least not now. * But it does finish the attribution of the override by attributing the * default initialization. * * @param tree * @param env */ public void finishOverrideAttribute(VisageOverrideClassVar tree, VisageEnv<VisageAttrContext> env) { VisageVarSymbol v = tree.sym; Type declType = tree.getId().type; result = tree.type = declType; // Need to check that the override did not specify a different type // w.r.t. the one that comes from overridden variable Type visageType = attribType(tree.getVisageType(), env); if (visageType != syms.visage_UnspecifiedType && !types.isSameType(declType, visageType)) { chk.typeError(tree.getVisageType().pos(), messages.getLocalizedString(MsgSym.MESSAGEPREFIX_COMPILER_MISC + MsgSym.MESSAGE_VISAGE_TYPED_OVERRIDE), visageType, declType); } else if (visageType == syms.visage_UnspecifiedType) { tree.setVisageType(visagemake.at(tree.pos).TypeClass(visagemake.at(tree.pos).Type(declType), types.isSequence(declType) ? Cardinality.ANY : Cardinality.SINGLETON)); } chk.checkBoundArrayVar(tree); if (types.isSameType(env.enclClass.type, v.owner.type)) { log.error(tree.getId().pos(), MsgSym.MESSAGE_VISAGE_CANNOT_OVERRIDE_OWN,tree.getId().getName()); } // The info.lint field in the envs stored in enter.typeEnvs is deliberately uninitialized, // because the annotations were not available at the time the env was created. Therefore, // we look up the environment chain for the first enclosing environment for which the // lint value is set. Typically, this is the parent env, but might be further if there // are any envs created as a result of TypeParameter nodes. VisageEnv<VisageAttrContext> lintEnv = env; while (lintEnv.info.lint == null) { lintEnv = lintEnv.next; } Lint lint = lintEnv.info.lint.augment(v.attributes_field, v.flags()); Lint prevLint = chk.setLint(lint); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); if ((v.flags() & VisageFlags.IS_DEF) != 0L) { log.error(tree.getId().pos(), MsgSym.MESSAGE_VISAGE_CANNOT_OVERRIDE_DEF,tree.getId().getName()); } else if (!rs.isAccessibleForWrite(env, env.enclClass.type, v)) { log.error(tree.getId().pos(), MsgSym.MESSAGE_VISAGE_CANNOT_OVERRIDE,tree.getId().getName()); } //TODO: (below) /*** * inBindContext is not implemented correctly here since things are not walked in tree order in finish*. * if (this.inBindContext) { v.flags_field |= VisageFlags.VARUSE_BOUND_INIT; } * */ try { VisageExpression init = tree.getInitializer(); if (init != null) { // Attribute initializer in a new environment/ // Check that initializer conforms to variable's declared type. Scope initScope = new Scope(new MethodSymbol(BLOCK, v.name, null, env.enclClass.sym)); initScope.next = env.info.scope; VisageEnv<VisageAttrContext> initEnv = env.dup(tree, env.info.dup(initScope)); initEnv.outer = env; // In order to catch self-references, we set the variable's // declaration position to maximal possible value, effectively // marking the variable as undefined. v.pos = Position.MAXPOS; chk.checkNonVoid(init, attribExpr(init, initEnv, declType)); chk.checkBidiBind(tree.getInitializer(), tree.getBindStatus(), initEnv, v.type); } } finally { chk.setLint(prevLint); log.useSource(prev); } } //@Override public void visitOverrideClassVar(VisageOverrideClassVar tree) { //TODO: handle static triggers VisageIdent id = tree.getId(); // let the owner of the environment be a freshly // created BLOCK-method. VisageEnv<VisageAttrContext> localEnv = newLocalEnv(tree); //find overridden var Type type = null; Symbol idSym = rs.findIdentInType(localEnv, localEnv.enclClass.type, tree.getName(), VAR); if (idSym != null) { idSym.complete(); id.sym = idSym; id.type = type = tree.type = idSym.type; if (idSym.kind < ERRONEOUS && idSym.kind == VAR) { tree.sym = (VisageVarSymbol)idSym; } else { //we couldn't find the overridden var log.error(id.pos(), MsgSym.MESSAGE_VISAGE_DECLARED_OVERRIDE_DOES_NOT, rs.kindName(VAR), tree.getName()); } } for (VisageOnReplace.Kind triggerKind : VisageOnReplace.Kind.values()) { VisageOnReplace trigger = tree.getTrigger(triggerKind); if (trigger != null) { if (triggerKind == VisageOnReplace.Kind.ONINVALIDATE) { localEnv.info.inInvalidate = true; localEnv.info.enclVar = idSym; } VisageVar oldValue = trigger.getOldValue(); if (oldValue != null && oldValue.type == null) { oldValue.type = type; } VisageVar newElements = trigger.getNewElements(); if (newElements != null && newElements.type == null) { newElements.type = type; } attribDecl(trigger, localEnv); } } if (idSym.kind < ERRONEOUS) { // Must reference an attribute if (idSym.kind != VAR) { log.error(id.pos(), MsgSym.MESSAGE_VISAGE_MUST_BE_AN_ATTRIBUTE,id.getName()); } else if (localEnv.outer.tree.getVisageTag() != VisageTag.CLASS_DEF) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_CANNOT_OVERRIDE_CLASS_VAR_FROM_FUNCTION, idSym.name, idSym.owner); } else { VisageVarSymbol v = (VisageVarSymbol) idSym; if (v.isOverridenIn(env.enclClass.sym)) { log.error(tree.getId().pos(), MsgSym.MESSAGE_VISAGE_DUPLICATE_VAR_OVERRIDE, idSym.name, idSym.owner); } else { v.addOverridingClass(env.enclClass.sym); tree.sym = v; if (tree.isBound()) { // If it is overridden with a bound, it is a bound init v.flags_field |= VisageFlags.VARUSE_BOUND_INIT; } finishOverrideAttribute(tree, env); } } } } //@Override public void visitOnReplace(VisageOnReplace tree) { Scope localScope = new Scope(new MethodSymbol(BLOCK, defs.lambda_MethodName, null, env.enclClass.sym)); VisageEnv<VisageAttrContext> localEnv = env.dup(tree, env.info.dup(localScope)); localEnv.outer = env; VisageVar lastIndex = tree.getLastIndex(); if (lastIndex != null) { lastIndex.mods.flags |= Flags.FINAL; attribVar(lastIndex, localEnv); lastIndex.sym.type = syms.intType; } VisageVar newElements = tree.getNewElements(); if (newElements != null) { newElements.mods.flags |= Flags.FINAL; attribVar(newElements, localEnv); } VisageVar firstIndex = tree.getFirstIndex(); if (firstIndex != null) { firstIndex.mods.flags |= Flags.FINAL; attribVar(firstIndex, localEnv); firstIndex.sym.type = syms.intType; } VisageVar oldValue = tree.getOldValue(); if (oldValue != null) { oldValue.mods.flags |= Flags.FINAL; attribVar(oldValue, localEnv); } attribExpr(tree.getBody(), localEnv); } private ArrayList<VisageForExpressionInClause> forClauses = null; //@Override public void visitForExpression(VisageForExpression tree) { VisageEnv<VisageAttrContext> forExprEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); forExprEnv.outer = env; if (forClauses == null) forClauses = new ArrayList<VisageForExpressionInClause>(); int forClausesOldSize = forClauses.size(); for (ForExpressionInClauseTree cl : tree.getInClauses()) { // Don't try to examine erroneous in clauses. We don't wish to // place the entire for expression into error nodes, just because // one or more in clauses was in error, so we jsut skip any // erroneous ones. // if (cl instanceof VisageErroneousForExpressionInClause) continue; VisageForExpressionInClause clause = (VisageForExpressionInClause)cl; forClauses.add(clause); VisageVar var = clause.getVar(); // Don't try to examine erroneous loop controls, such as // when a variable was missing. Again, this is because the IDE may // try to attribute a node that is mostly correct, but contains // one or more components that are in error. // if (var == null || var instanceof VisageErroneousVar) continue; Type declType = attribType(var.getVisageType(), forExprEnv); attribVar(var, forExprEnv); VisageExpression expr = (VisageExpression)clause.getSequenceExpression(); Type exprType = types.upperBound(attribExpr(expr, forExprEnv)); chk.checkNonVoid(((VisageTree)clause).pos(), exprType); Type elemtype; // if exprtype is T[], T is the element type of the for-each if (types.isSequence(exprType)) { elemtype = types.elementType(exprType); } // if exprtype implements Iterable<T>, T is the element type of the for-each else if (types.asSuper(exprType, syms.iterableType.tsym) != null) { if (clause.isBound()) { log.error(clause.getSequenceExpression().pos(), MsgSym.MESSAGE_VISAGE_FOR_OVER_ITERABLE_DISALLOWED_IN_BIND); } Type base = types.asSuper(exprType, syms.iterableType.tsym); List<Type> iterableParams = base.allparams(); if (iterableParams.isEmpty()) { elemtype = syms.objectType; } else { elemtype = types.upperBound(iterableParams.last()); } } //FIXME: if exprtype is nativearray of T, T is the element type of the for-each (see VSGC-2784) else if (types.isArray(exprType)) { elemtype = types.elemtype(exprType); } else { log.warning(expr, MsgSym.MESSAGE_VISAGE_ITERATING_NON_SEQUENCE); elemtype = exprType; } if (elemtype == syms.errType) { log.error(clause.getSequenceExpression().pos(), MsgSym.MESSAGE_FOREACH_NOT_APPLICABLE_TO_TYPE); } else if (elemtype == syms.botType || elemtype == syms.unreachableType) { elemtype = syms.objectType; } else { // if it is a primitive type, unbox it Type unboxed = types.unboxedType(elemtype); if (unboxed != Type.noType) { elemtype = unboxed; } chk.checkType(clause.getSequenceExpression().pos(), elemtype, declType, Sequenceness.DISALLOWED); } if (declType == syms.visage_UnspecifiedType) { var.type = elemtype; var.sym.type = elemtype; } if (clause.getWhereExpression() != null) { attribExpr(clause.getWhereExpression(), env, syms.booleanType); } } forExprEnv.tree = tree; // before, we were not in loop! attribTree(tree.getBodyExpression(), forExprEnv, VAL, pt.tag != ERROR ? pt : Type.noType, Sequenceness.PERMITTED); if (!tree.getBodyExpression().type.isErroneous() && tree.getBodyExpression().type.tag != VOID && (pt.tag == NONE || pt == syms.visage_UnspecifiedType)) { Type blockElemType = types.elementTypeOrType(tree.getBodyExpression().type); Type typeToCheck = types.sequenceType(types.normalize(blockElemType)); attribTree(tree.getBodyExpression(), forExprEnv, VAL, typeToCheck, Sequenceness.PERMITTED); } Type bodyType = tree.getBodyExpression().type; if (bodyType == syms.unreachableType) log.error(tree.getBodyExpression(), MsgSym.MESSAGE_UNREACHABLE_STMT); Type owntype = (bodyType == null || bodyType == syms.voidType)? syms.voidType : types.isSequence(bodyType) ? bodyType : types.sequenceType(bodyType); while (forClauses.size() > forClausesOldSize) forClauses.remove(forClauses.size()-1); forExprEnv.info.scope.leave(); result = check(tree, owntype, VAL, pkind, pt, pSequenceness); } //@Override public void visitForExpressionInClause(VisageForExpressionInClause that) { // Do not assert that we cannot reach here as this unit can // be visited by virtue of visiting VisageErronous which // will attempt to visit each Erroneous node that it has // encapsualted. // } public void visitIndexof(VisageIndexof tree) { for (int n = forClauses == null ? 0 : forClauses.size(); ; ) { if (--n < 0) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_INDEXOF_NOT_FOUND,tree.fname.getName()); break; } VisageForExpressionInClause clause = forClauses.get(n); // Don't try to examine erroneous in clauses. We don't wish to // place the entire for expression into error nodes, just because // one or more in clauses was in error, so we jsut skip any // erroneous ones. // if (clause == null || clause instanceof VisageErroneousForExpressionInClause) continue; VisageVar v = clause.getVar(); // Don't try to deal with Erroneous or missing variables // if (v == null || v instanceof VisageErroneousVar) continue; if (clause.getVar().getName() == tree.fname.getName()) { tree.clause = clause; tree.fname.sym = clause.getVar().sym; clause.setIndexUsed(true); break; } } result = check(tree, syms.visage_IntegerType, VAL, pkind, pt, pSequenceness); } //@Override public void visitSkip(VisageSkip tree) { result = syms.voidType; tree.type = result; } //@Override public void visitBlockExpression(VisageBlock tree) { // Create a new local environment with a local scope. VisageEnv<VisageAttrContext> localEnv; if (env.info.scope.owner.kind == TYP) { // Block is a static or instance initializer; // let the owner of the environment be a freshly // created BLOCK-method. localEnv = newLocalEnv(tree); if ((tree.flags & STATIC) != 0) { localEnv.info.staticLevel++; } } else { Scope localScope = new Scope(env.info.scope.owner); localScope.next = env.info.scope; localEnv = env.dup(tree, env.info.dup(localScope)); localEnv.outer = env; if (env.tree instanceof VisageFunctionDefinition) { if (env.enclClass.runMethod == env.tree) env.enclClass.runBodyScope = localEnv.info.scope; localEnv.info.scope.owner = env.info.scope.owner; } else { localEnv.info.scope.owner = new MethodSymbol(BLOCK, names.empty, null, env.enclClass.sym); } } memberEnter.memberEnter(tree.getStmts(), localEnv); if (tree.getValue() != null) { memberEnter.memberEnter(tree.getValue(), localEnv); } boolean canReturn = true; boolean unreachableReported = false; tree.type = syms.visage_UnspecifiedType; for (List<VisageExpression> l = tree.stats; l.nonEmpty(); l = l.tail) { if (! canReturn && ! unreachableReported) { unreachableReported = true; log.error(l.head.pos(), MsgSym.MESSAGE_UNREACHABLE_STMT); } Type stype = attribExpr(l.head, localEnv); if (stype == syms.unreachableType) canReturn = false; } Type owntype = null; if (tree.value != null) { if (!canReturn && !unreachableReported) { log.error(tree.value.pos(), MsgSym.MESSAGE_UNREACHABLE_STMT); } Type valueType = attribExpr(tree.value, localEnv, pt, pSequenceness); if (valueType == syms.voidType && !tree.isVoidValueAllowed) { //void value not allowed in a block that has non-void return log.warning(tree.value.pos(), MsgSym.MESSAGE_VISAGE_VOID_BLOCK_VALUE_NOT_ALLOWED); owntype = tree.type; } else { owntype = valueType != syms.unreachableType ? unionType(tree.pos(), tree.type, valueType) : syms.unreachableType; } } if (owntype == null) { owntype = syms.voidType; } if (!canReturn) { owntype = syms.unreachableType; } owntype = owntype.baseType(); result = check(tree, owntype, VAL, pkind, pt, pSequenceness, false); if (env.info.scope.owner.kind != TYP) localEnv.info.scope.leave(); } /** * @param tree */ //@Override public void visitWhileLoop(VisageWhileLoop tree) { attribExpr(tree.cond, env, syms.booleanType); attribExpr(tree.body, env.dup(tree)); result = syms.voidType; tree.type = result; } //@Override public void visitInstanciate(VisageInstanciate tree) { Type owntype = syms.errType; // The local environment of a class creation is // a new environment nested in the current one. VisageEnv<VisageAttrContext> localEnv = newLocalEnv(tree); List<VisageVar> vars = tree.getLocalvars(); memberEnter.memberEnter(vars, localEnv); for (List<VisageVar> l = vars; l.nonEmpty(); l = l.tail) attribExpr(l.head, localEnv); // The anonymous inner class definition of the new expression, // if one is defined by it. VisageClassDeclaration cdef = tree.getClassBody(); // If enclosing class is given, attribute it, and // complete class name to be fully qualified VisageExpression clazz = tree.getIdentifier(); // Class field following new // Attribute clazz expression Type clazztype = attribType(clazz, env); /* MAYBE FUTURE, e.g. if we support the syntax 'new ARRAY_TYPE (COUNT)': if (tree.getVisageKind() == VisageKind.INSTANTIATE_NEW && clazztype.tag == ARRAY) { if (tree.getArgs().size() != 1) log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_NEW_ARRAY_MUST_HAVE_SINGLE_ARG); else attribExpr(tree.getArgs().head, env, syms.visage_IntegerType); result = check(tree, clazztype, VAL, pkind, pt, pSequenceness); localEnv.info.scope.leave(); return; } If so, add to MsgSym.java this definition: public static final String MESSAGE_VISAGE_NEW_ARRAY_MUST_HAVE_SINGLE_ARG = "visage.new.array.must.have.single.arg"; and in visagecompiler.properties map that to: Allocating a native array requires a single length parameter. */ // Store symbol + type back into the attributed tree. clazztype = chk.checkClassType( clazz.pos(), clazztype, true); chk.validate(clazz); if (!clazztype.tsym.isInterface() && clazztype.getEnclosingType().tag == CLASS) { // Check for the existence of an apropos outer instance rs.resolveImplicitThis(tree.pos(), env, clazztype); } // Attribute constructor arguments. List<Type> argtypes = attribArgs(tree.getArgs(), localEnv); // If we have made no mistakes in the class type... if (clazztype.tag == CLASS) { // Check that class is not abstract or mixin long flags = clazztype.tsym.flags(); if (cdef == null && (flags & (ABSTRACT | INTERFACE | VisageFlags.MIXIN)) != 0) { if ((flags & (VisageFlags.MIXIN)) != 0) { // VSGC-2815 - new expressions should report an error when trying to instantiate a mixin class. log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_MIXIN_CANNOT_BE_INSTANTIATED, clazztype.tsym); } else { log.error(tree.pos(), MsgSym.MESSAGE_ABSTRACT_CANNOT_BE_INSTANTIATED, clazztype.tsym); } } else if (cdef != null && clazztype.tsym.isInterface()) { // Check that no constructor arguments are given to // anonymous classes implementing an interface if (!argtypes.isEmpty()) log.error(tree.getArgs().head.pos(), MsgSym.MESSAGE_ANON_CLASS_IMPL_INTF_NO_ARGS); // Error recovery: pretend no arguments were supplied. argtypes = List.nil(); } else if (types.isVisageClass(clazztype.tsym) && tree.getArgs().nonEmpty()) { log.error(tree.getArgs().head.pos(), MsgSym.MESSAGE_NEW_VISAGE_CLASS_NO_ARGS); } // Resolve the called constructor under the assumption // that we are referring to a superclass instance of the // current instance (JLS ???). else { localEnv.info.selectSuper = cdef != null && cdef.getMembers().nonEmpty(); localEnv.info.varArgs = false; if (! types.isVisageClass(clazztype.tsym)) tree.constructor = rs.resolveConstructor( tree.pos(), localEnv, clazztype, argtypes, null); /** List<Type> emptyTypeargtypes = List.<Type>nil(); tree.constructor = rs.resolveConstructor( tree.pos(), localEnv, clazztype, argtypes, emptyTypeargtypes); Type ctorType = checkMethod(clazztype, tree.constructor, localEnv, tree.getArguments(), argtypes, emptyTypeargtypes, localEnv.info.varArgs); if (localEnv.info.varArgs) assert ctorType.isErroneous(); * ***/ } if (((ClassSymbol) clazztype.tsym).fullname == defs.cJavaLangThreadName) { log.warning(tree.pos(), MsgSym.MESSAGE_VISAGE_EXPLICIT_THREAD); } if (cdef != null) { // We are seeing an anonymous class instance creation. // In this case, the class instance creation // expression // // E.new <typeargs1>C<typargs2>(args) { ... } // // is represented internally as // // E . new <typeargs1>C<typargs2>(args) ( class <empty-name> { ... } ) . // // This expression is then *transformed* as follows: // // (1) add a STATIC flag to the class definition // if the current environment is static // (2) add an extends or implements clause // (3) add a constructor. // // For instance, if C is a class, and ET is the type of E, // the expression // // E.new <typeargs1>C<typargs2>(args) { ... } // // is translated to (where X is a fresh name and typarams is the // parameter list of the super constructor): // // new <typeargs1>X(<*nullchk*>E, args) where // X extends C<typargs2> { // <typarams> X(ET e, args) { // e.<typeargs1>super(args) // } // ... // } if (cdef.sym == null) enter.classEnter(cdef, env); attribDecl(cdef, localEnv); // Reassign clazztype and recompute constructor. clazztype = cdef.sym.type; Symbol sym = rs.resolveConstructor( tree.pos(), localEnv, clazztype, argtypes, List.<Type>nil(), true, false); tree.constructor = sym; } // if (tree.constructor != null && tree.constructor.kind == MTH) owntype = clazz.type; // this give declared type, where clazztype would give anon type } Scope partsScope = new Scope(clazztype.tsym); for (VisageObjectLiteralPart localPt : tree.getParts()) { // Protect against erroneous nodes // if (localPt == null) continue; VisageObjectLiteralPart part = (VisageObjectLiteralPart)localPt; Symbol memberSym = rs.findIdentInType(env, clazz.type, part.name, VAR); memberSym = rs.access(memberSym, localPt.pos(), clazz.type, part.name, true); memberSym.complete(); Scope.Entry oldEntry = partsScope.lookup(memberSym.name); if (oldEntry.sym != null) { log.error(localPt.pos(), MsgSym.MESSAGE_VISAGE_ALREADY_DEFINED_OBJECT_LITERAL, memberSym); } partsScope.enter(memberSym); Type memberType = memberSym.type; if (!(memberSym instanceof VisageVarSymbol) ) { log.error(localPt.pos(), MsgSym.MESSAGE_VISAGE_INVALID_ASSIGNMENT); memberType = Type.noType; } Scope initScope = new Scope(new MethodSymbol(BLOCK, memberSym.name, null, env.enclClass.sym)); initScope.next = env.info.scope; VisageEnv<VisageAttrContext> initEnv = env.dup(localPt, env.info.dup(initScope)); initEnv.outer = localEnv; // Protect against erroneous tress called for attribution from the IDE // if (part.getExpression() != null) { attribExpr(part.getExpression(), initEnv, memberType); if (types.isArray(part.getExpression().type) && part.isBound()) { log.warning(part.pos(), MsgSym.MESSAGE_VISAGE_UNSUPPORTED_TYPE_IN_BIND); } } if (memberSym instanceof VisageVarSymbol) { VisageVarSymbol v = (VisageVarSymbol) memberSym; if (v.isStatic()) { log.error(localPt.pos(), MsgSym.MESSAGE_VISAGE_CANNOT_INIT_STATIC_OBJECT_LITERAL, memberSym); } WriteKind kind = part.isExplicitlyBound() ? WriteKind.INIT_BIND : WriteKind.INIT_NON_BIND; chk.checkAssignable(part.pos(), v, part, clazz.type, localEnv, kind); chk.checkBidiBind(part.getExpression(), part.getBindStatus(), localEnv, v.type); } part.type = memberType; part.sym = memberSym; } result = check(tree, owntype, VAL, pkind, pt, pSequenceness); localEnv.info.scope.leave(); } /** Make an attributed null check tree. */ public VisageExpression makeNullCheck(VisageExpression arg) { // optimization: X.this is never null; skip null check Name name = VisageTreeInfo.name(arg); if (name == names._this || name == names._super) return arg; VisageTag optag = VisageTag.NULLCHK; VisageUnary tree = visagemake.at(arg.pos).Unary(optag, arg); tree.operator = syms.nullcheck; tree.type = arg.type; return tree; } //@Override public void visitFunctionValue(VisageFunctionValue tree) { VisageFunctionDefinition def = new VisageFunctionDefinition(visagemake.Modifiers(Flags.SYNTHETIC), defs.lambda_MethodName, tree); def.pos = tree.pos; tree.definition = def; MethodSymbol m = new MethodSymbol(SYNTHETIC, def.name, null, env.enclClass.sym); // m.flags_field = chk.checkFlags(def.pos(), def.mods.flags, m, def); def.sym = m; finishFunctionDefinition(def, env); result = tree.type = check(tree, syms.makeFunctionType((MethodType) def.type), VAL, pkind, pt, pSequenceness); } //@Override public void visitFunctionDefinition(VisageFunctionDefinition tree) { // Tree may come in paritally complete or in error from IDE and so we protect // against it. Do nothing if the tree isn't properly attributed. // if (tree.sym != null) { MethodSymbol m = tree.sym; m.complete(); warnOnStaticUse(tree.pos(), tree.getModifiers(), m); } } /** Search super-clases for a parameter type in a matching method. * The idea is that when a formal parameter isn't specified in a class * function, see if there is a method with the same name in a superclass, * and use that method's parameter type. If there are multiple methods * in super-classes that all have the same name and argument count, * the parameter types have to be the same in all of them. * @param csym Class to search. * @param name Name of matching methods. * @param paramCount Number of parameters of matching methods. * @param paramNum The parameter number we're concerned about, * or -1 if we're searching for the return type. * @return The found type. Null is we found no match. * Notype if we found an ambiguity. */ private Type searchSupersForParamType (ClassSymbol c, Name name, int paramCount, int paramNum) { Type found = null; for (Scope.Entry e = c.members().lookup(name); e.scope != null; e = e.next()) { if ((e.sym.kind & MTH) == 0 || (e.sym.flags_field & (STATIC|SYNTHETIC)) != 0) continue; Type mt = types.memberType(c.type, e.sym); if (mt == null) continue; List<Type> formals = mt.getParameterTypes(); if (formals.size() != paramCount) continue; Type t = paramNum >= 0 ? formals.get(paramNum) : mt.getReturnType(); if (t == Type.noType) return t; if (found == null) found = t; else if (t != null && found != t) return Type.noType; } Type st = types.supertype(c.type); if (st.tag == CLASS) { Type t = searchSupersForParamType((ClassSymbol)st.tsym, name, paramCount, paramNum); if (t == Type.noType) return t; if (found == null) found = t; else if (t != null && found != t) return Type.noType; } for (List<Type> l = types.interfaces(c.type); l.nonEmpty(); l = l.tail) { Type t = searchSupersForParamType((ClassSymbol)l.head.tsym, name, paramCount, paramNum); if (t == Type.noType) return t; if (found == null) found = t; else if (t != null && found != t) return Type.noType; } return found; } public void finishFunctionDefinition(VisageFunctionDefinition tree, VisageEnv<VisageAttrContext> env) { MethodSymbol m = tree.sym; VisageFunctionValue opVal = tree.operation; VisageEnv<VisageAttrContext> localEnv = memberEnter.methodEnv(tree, env); Type returnType; // Create a new environment with local scope // for attributing the method. VisageEnv<VisageAttrContext> lintEnv = env; while (lintEnv.info.lint == null) lintEnv = lintEnv.next; JavaFileObject prev = log.useSource(env.toplevel.sourcefile); Lint lint = lintEnv.info.lint.augment(m.attributes_field, m.flags()); Lint prevLint = chk.setLint(lint); try { localEnv.info.lint = lint; ClassSymbol owner = env.enclClass.sym; if ((owner.flags() & ANNOTATION) != 0 && tree.operation.funParams.nonEmpty()) log.error(tree.operation.funParams.head.pos(), MsgSym.MESSAGE_INTF_ANNOTATION_MEMBERS_CANNOT_HAVE_PARAMS); // Attribute all value parameters. ListBuffer<Type> argbuf = new ListBuffer<Type>(); List<Type> pparam = null; MethodType mtype = null; if (pt.tag == TypeTags.METHOD || pt instanceof FunctionType) { mtype = pt.asMethodType(); pparam = mtype.getParameterTypes(); } int paramNum = 0; List<VisageVar> params = tree.getParams(); int paramCount = params.size(); for (List<VisageVar> l = params; l.nonEmpty(); l = l.tail) { VisageVar pvar = l.head; // Don't try to deal with parameters that are Erroneous // or missing, which can happen when the IDE is trying to // make sense of a partially completed function definition // if ( pvar == null // Not even present || pvar.getVisageType() == null // There, but can't do anythign about typing it || pvar.getVisageType() instanceof VisageErroneousType // There, but flagged as an erroneous type for some syntactic reason ) continue; // Hence, we can't do anythign with this parameter definitionm skip it Type type; if (pparam != null && pparam.nonEmpty()) { type = pparam.head; pparam = pparam.tail; } else { type = syms.objectType; if (pvar.getVisageType() instanceof VisageTypeUnknown) { Type t = searchSupersForParamType (owner, m.name, paramCount, paramNum); if (t == Type.noType) log.warning(pvar.pos(), MsgSym.MESSAGE_VISAGE_AMBIGUOUS_PARAM_TYPE_FROM_SUPER); else if (t != null) type = t; } } pvar.type = type; type = chk.checkNonVoid(pvar, attribVar(pvar, localEnv)); argbuf.append(type); paramNum++; } returnType = syms.unknownType; if (opVal.getVisageReturnType().getVisageTag() != VisageTag.TYPEUNKNOWN) returnType = attribType(tree.getVisageReturnType(), localEnv); else if (mtype != null) { Type mrtype = mtype.getReturnType(); if (mrtype != null && mrtype.tag != TypeTags.NONE) returnType = mrtype; } else { // If we made use of the parameter types to select a matching // method, we could presumably get a non-ambiguoys return type. // But this is pretty close, in practice. Type t = searchSupersForParamType (owner, m.name, paramCount, -1); if (t == Type.noType) log.warning(tree.pos(), MsgSym.MESSAGE_VISAGE_AMBIGUOUS_RETURN_TYPE_FROM_SUPER); else if (t != null) returnType = t; } if (returnType == syms.visage_java_lang_VoidType) returnType = syms.voidType; mtype = new MethodType(argbuf.toList(), returnType, // may be unknownType List.<Type>nil(), syms.methodClass); m.type = mtype; if (m.owner instanceof ClassSymbol) { // Fix primitive/number types so overridden Java methods will have the correct types. fixOverride(tree, m, true); if (returnType == syms.unknownType) { returnType = m.getReturnType(); } } if (tree.getBodyExpression() == null) { // Empty bodies are only allowed for // abstract, native, or interface methods, or for methods // in a retrofit signature class. if ((owner.flags() & INTERFACE) == 0 && (tree.mods.flags & (ABSTRACT | NATIVE)) == 0 && !relax) log.error(tree.pos(), MsgSym.MESSAGE_MISSING_METH_BODY_OR_DECL_ABSTRACT); else if (returnType == syms.unknownType) // no body, can't infer, assume Any // FIXME Should this be Void or an error? returnType = syms.visage_AnyType; } else if ((owner.flags() & INTERFACE) != 0) { log.error(tree.getBodyExpression().pos(), MsgSym.MESSAGE_INTF_METH_CANNOT_HAVE_BODY); } else if ((tree.mods.flags & ABSTRACT) != 0) { log.error(tree.pos(), MsgSym.MESSAGE_ABSTRACT_METH_CANNOT_HAVE_BODY); } else if ((tree.mods.flags & NATIVE) != 0) { log.error(tree.pos(), MsgSym.MESSAGE_NATIVE_METH_CANNOT_HAVE_BODY); } else { VisageBlock body = opVal.getBodyExpression(); if (body.value instanceof VisageReturn) { if (returnType == syms.voidType) { log.error(body.value.pos(), MsgSym.MESSAGE_CANNOT_RET_VAL_FROM_METH_DECL_VOID); } /* * We are going to rewrite blocks value as an expression instead * of original return statement. So, we better save the original * return statement so that we can present it to external tree * walkers, if needed. */ body.returnStatement = (VisageReturn) body.value; body.value = ((VisageReturn) body.value).expr; } // Attribute method bodyExpression Type typeToCheck = returnType; if(tree.name == defs.internalRunFunctionName) { typeToCheck = Type.noType; } else if (returnType == syms.voidType) { typeToCheck = Type.noType; } Type bodyType = attribExpr(body, localEnv, typeToCheck); // Special handling for the run function. Its body is empty at this point. if (body.value == null) { if (returnType == syms.unknownType) returnType = syms.visage_VoidType; //TODO: this is wrong if there is a return statement } else { if (returnType == syms.unknownType) returnType = bodyType == syms.unreachableType ? syms.visage_VoidType : types.normalize(bodyType); else if (returnType != syms.visage_VoidType && tree.getName() != defs.internalRunFunctionName) chk.checkType(tree.pos(), bodyType, returnType, Sequenceness.PERMITTED, false); } if (tree.isBound() && returnType == syms.visage_VoidType) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_BOUND_FUNCTION_MUST_NOT_BE_VOID); } } localEnv.info.scope.leave(); mtype.restype = returnType; result = tree.type = mtype; // If we override any other methods, check that we do so properly. // JLS ??? if (m.owner instanceof ClassSymbol) { chk.checkOverride(tree, m); } else { if ((m.flags() & VisageFlags.OVERRIDE) != 0) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_DECLARED_OVERRIDE_DOES_NOT, rs.kindName(m), m); } } } finally { chk.setLint(prevLint); log.useSource(prev); } // mark the method varargs, if necessary // if (isVarArgs) m.flags_field |= Flags.VARARGS; // Set the inferred types in the MethodType.argtypes and in correct symbols in MethodSymbol List<VarSymbol> paramSyms = List.<VarSymbol>nil(); List<Type> paramTypes = List.<Type>nil(); for (VisageVar var : tree.getParams()) { // Skip erroneous parameters, which happens if the IDE is calling with a // a paritally defined function. // if (var == null || var.type == null) continue; paramSyms = paramSyms.append(var.sym); paramTypes = paramTypes.append(var.type); } m.params = paramSyms; if (m.type != null && m.type instanceof MethodType) { ((MethodType)m.type).argtypes = paramTypes; } } //@Override public void visitTry(VisageTry tree) { boolean canReturn = false; // Attribute body Type stype = attribExpr(tree.body, env.dup(tree, env.info.dup())); if (stype != syms.unreachableType) canReturn = true; // Attribute catch clauses for (List<VisageCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { VisageCatch c = l.head; if (c == null) continue; // Don't try to handle erroneous catch blocks VisageEnv<VisageAttrContext> catchEnv = newLocalEnv(c); memberEnter.memberEnter(c.param, catchEnv); if (c.param == null) continue; // Don't try to handle erroneous catch blocks if (c.param.type == null) c.param.sym.type = c.param.type = syms.throwableType; Type ctype = attribDecl((VisageVar) c.param, catchEnv); if (c.param.type.tsym.kind == Kinds.VAR) { c.param.sym.setData(ElementKind.EXCEPTION_PARAMETER); } //uses vartype // chk.checkType(c.param.vartype.pos(), // chk.checkClassType(c.param.vartype.pos(), ctype), // syms.throwableType); ctype = attribExpr(c.body, catchEnv); if (ctype != syms.unreachableType) canReturn = true; } // Attribute finalizer if (tree.finalizer != null) attribExpr(tree.finalizer, env); result = canReturn ? syms.voidType : syms.unreachableType; tree.type = result; } //@Override public void visitIfExpression(VisageIfExpression tree) { attribExpr(tree.cond, env, syms.booleanType); attribTree(tree.truepart, env, VAL, pt, pSequenceness); Type falsepartType = tree.falsepart != null ? attribTree(tree.falsepart, env, VAL, pt, pSequenceness) : syms.voidType; result = check(tree, condType(tree.pos(), tree.cond.type, tree.truepart.type, falsepartType), VAL, pkind, pt, pSequenceness); } //where /** Compute the type of a conditional expression, after * checking that it exists. See Spec 15.25. * * @param pos The source position to be used for * error diagnostics. * @param condtype The type of the expression's condition. * @param type1 The type of the expression's then-part. * @param type2 The type of the expression's else-part. */ private Type condType(DiagnosticPosition pos, Type condtype, Type thentype, Type elsetype) { Type ctype = unionType(pos, thentype, elsetype); // If condition and both arms are numeric constants, // evaluate at compile-time. return ((condtype.constValue() != null) && (thentype.constValue() != null) && (elsetype.constValue() != null)) ? cfolder.coerce(condtype.isTrue()?thentype:elsetype, ctype) : ctype; } /** Compute the type of a conditional expression, after * checking that it exists. Does not take into * account the special case where condition and both arms * are constants. * * @param pos The source position to be used for error * diagnostics. * @param condtype The type of the expression's condition. * @param type1 The type of the expression's then-part. * @param type2 The type of the expression's else-part. */ private Type unionType(DiagnosticPosition pos, Type type1, Type type2) { if (type1 == syms.unreachableType || type1 == syms.visage_UnspecifiedType) return type2; if (type2 == syms.unreachableType || type2 == syms.visage_UnspecifiedType) return type1; if (type1 == type2) return type1; // Ensure that we don't NPE if either of the inputs were from // Erroneous nodes such as missing blocks on conditionals and so on. // if (type1 == null ) { if (type2 == null) { return syms.voidType; } else { return type2; } } else if (type2 == null) { return type1; } if (type1.tag == VOID || type2.tag == VOID) return syms.voidType; boolean isSequence1 = types.isSequence(type1); boolean isSequence2 = types.isSequence(type2); if (isSequence1 || isSequence2) { if (isSequence1) type1 = types.elementType(type1); if (isSequence2) type2 = types.elementType(type2); Type union = unionType(pos, type1, type2); return union.tag == ERROR ? union : types.sequenceType(union); } // If same type, that is the result if (types.isSameType(type1, type2)) return type1.baseType(); Type thenUnboxed = (!allowBoxing || type1.isPrimitive()) ? type1 : types.unboxedType(type1); Type elseUnboxed = (!allowBoxing || type2.isPrimitive()) ? type2 : types.unboxedType(type2); // Otherwise, if both arms can be converted to a numeric // type, return the least numeric type that fits both arms // (i.e. return larger of the two, or return int if one // arm is short, the other is char). if (thenUnboxed.isPrimitive() && elseUnboxed.isPrimitive()) { // If one arm has an integer subrange type (i.e., byte, // short, or char), and the other is an integer constant // that fits into the subrange, return the subrange type. if (thenUnboxed.tag < INT && elseUnboxed.tag == INT && types.isAssignable(elseUnboxed, thenUnboxed)) return thenUnboxed.baseType(); if (elseUnboxed.tag < INT && thenUnboxed.tag == INT && types.isAssignable(thenUnboxed, elseUnboxed)) return elseUnboxed.baseType(); for (int i = BYTE; i < VOID; i++) { Type candidate = syms.typeOfTag[i]; if (types.isSubtype(thenUnboxed, candidate) && types.isSubtype(elseUnboxed, candidate)) return candidate; } } // Those were all the cases that could result in a primitive if (allowBoxing) { type1 = types.boxedTypeOrType(type1); type2 = types.boxedTypeOrType(type2); } if (types.isSubtype(type1, type2)) return type2.baseType(); if (types.isSubtype(type2, type1)) return type1.baseType(); if (!allowBoxing) { log.error(pos, MsgSym.MESSAGE_NEITHER_CONDITIONAL_SUBTYPE, type1, type2); return type1.baseType(); } // both are known to be reference types. The result is // lub(type1,type2). This cannot fail, as it will // always be possible to infer "Object" if nothing better. return types.makeUnionType(type1, type2); } //@Override public void visitBreak(VisageBreak tree) { tree.target = findJumpTarget(tree.pos(), tree.getVisageTag(), tree.label, env); result = tree.type = syms.unreachableType; } //@Override public void visitContinue(VisageContinue tree) { tree.target = findJumpTarget(tree.pos(), tree.getVisageTag(), tree.label, env); result = tree.type = syms.unreachableType; } //where /** Return the target of a break or continue statement, if it exists, * report an error if not. * Note: The target of a labelled break or continue is the * (non-labelled) statement tree referred to by the label, * not the tree representing the labelled statement itself. * * @param pos The position to be used for error diagnostics * @param tag The tag of the jump statement. This is either * Tree.BREAK or Tree.CONTINUE. * @param label The label of the jump statement, or null if no * label is given. * @param env The environment current at the jump statement. */ private VisageTree findJumpTarget(DiagnosticPosition pos, VisageTag tag, Name label, VisageEnv<VisageAttrContext> env) { // Search environments outwards from the point of jump. VisageEnv<VisageAttrContext> env1 = env; LOOP: while (env1 != null) { switch (env1.tree.getVisageTag()) { case WHILELOOP: case FOR_EXPRESSION: if (label == null) return env1.tree; break; default: } env1 = env1.next; } if (label != null) log.error(pos, MsgSym.MESSAGE_UNDEF_LABEL, label); else if (tag == VisageTag.CONTINUE) log.error(pos, MsgSym.MESSAGE_CONT_OUTSIDE_LOOP); else log.error(pos, MsgSym.MESSAGE_BREAK_OUTSIDE_SWITCH_LOOP); return null; } //@Override public void visitReturn(VisageReturn tree) { if (env.enclFunction == null) { log.error(tree.pos(), MsgSym.MESSAGE_RETURN_OUTSIDE_METH); } else { // Attribute return expression, if it exists, and check that // it conforms to result type of enclosing method. Symbol m = env.enclFunction.sym; tree.returnType = m.type.getReturnType(); VisageBlock enclBlock = env.enclFunction.operation.bodyExpression; if (tree.returnType == null) log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_CANNOT_INFER_RETURN_TYPE); else if (tree.returnType.tag == VOID) { if (tree.expr != null) { log.error(tree.pos(), MsgSym.MESSAGE_CANNOT_RET_VAL_FROM_METH_DECL_VOID); } } else if (tree.expr == null) { if (enclBlock.type == syms.visage_UnspecifiedType) enclBlock.type = syms.visage_VoidType; else if (enclBlock.type != syms.visage_VoidType) log.error(tree.pos(), MsgSym.MESSAGE_MISSING_RET_VAL); } else { if (enclBlock.type.tag == VOID) { log.error(tree.pos(), MsgSym.MESSAGE_CANNOT_RET_VAL_FROM_METH_DECL_VOID); } Type exprType = attribExpr(tree.expr, env); enclBlock.type = unionType(tree.pos(), enclBlock.type, exprType); enclBlock.isVoidValueAllowed = false; } } result = tree.type = syms.unreachableType; } //@Override public void visitThrow(VisageThrow tree) { if (tree.expr != null && !(tree.expr instanceof VisageErroneous)) { attribExpr(tree.expr, env, syms.throwableType); } result = tree.type = syms.unreachableType; } private void searchParameterTypes (VisageExpression meth, Type[] paramTypes) { // FUTURE: Search for matching overloaded methods/functions that // would be a match for meth, and number of arguments==paramTypes.length. // If all the candidates have the same type for parameter # i, // set paramTypes[i] to that type. // Otherwise, leave paramTypes[i]==null. } //@Override public void visitFunctionInvocation(VisageFunctionInvocation tree) { // The local environment of a method application is // a new environment nested in the current one. VisageEnv<VisageAttrContext> localEnv = env.dup(tree, env.info.dup()); localEnv.outer = env; // The types of the actual method type arguments. List<Type> typeargtypes; Name methName = VisageTreeInfo.name(tree.meth); int argcount = tree.args.size(); Type[] paramTypes = new Type[argcount]; searchParameterTypes(tree.meth, paramTypes); ListBuffer<Type> argtypebuffer = new ListBuffer<Type>(); int i = 0; for (List<VisageExpression> l = tree.args; l.nonEmpty(); l = l.tail, i++) { Type argtype = paramTypes[i]; if (argtype != null) attribExpr(l.head, env, argtype); else argtype = chk.checkNonVoid(l.head.pos(), types.upperBound(attribTree(l.head, env, VAL, Infer.anyPoly, Sequenceness.PERMITTED))); argtypebuffer.append(argtype); } List<Type> argtypes = argtypebuffer.toList(); typeargtypes = attribTypes(tree.typeargs, localEnv); // ... and attribute the method using as a prototype a methodtype // whose formal argument types is exactly the list of actual // arguments (this will also set the method symbol). Type mpt = new MethodType(argtypes, pt, null, syms.methodClass); if (typeargtypes.nonEmpty()) mpt = new ForAll(typeargtypes, mpt); localEnv.info.varArgs = false; Type mtype = attribExpr(tree.meth, localEnv, mpt); if (localEnv.info.varArgs) assert mtype.isErroneous() || tree.varargsElement != null; // Compute the result type. Type restype = mtype.getReturnType(); if (restype == syms.unknownType) { log.error(tree.meth.pos(), MsgSym.MESSAGE_VISAGE_FUNC_TYPE_INFER_CYCLE, methName); restype = syms.objectType; } // as a special case, array.clone() has a result that is // the same as static type of the array being cloned if (tree.meth.getVisageTag() == VisageTag.SELECT && allowCovariantReturns && methName == names.clone && types.isArray(((VisageSelect) tree.meth).selected.type)) restype = ((VisageSelect) tree.meth).selected.type; // as a special case, x.getClass() has type Class<? extends |X|> if (allowGenerics && methName == names.getClass && tree.args.isEmpty()) { Type qualifier = (tree.meth.getVisageTag() == VisageTag.SELECT) ? ((VisageSelect) tree.meth).selected.type : env.enclClass.sym.type; qualifier = types.boxedTypeOrType(qualifier); restype = new ClassType(restype.getEnclosingType(), List.<Type>of(new WildcardType(types.erasure(qualifier), BoundKind.EXTENDS, syms.boundClass)), restype.tsym); } if (mtype instanceof ErrorType) { tree.type = mtype; result = mtype; } else if (mtype instanceof MethodType || mtype instanceof FunctionType) { // If the "method" has a symbol, we've already checked for // formal/actual consistency. So doing it again would be // wasteful - plus varargs hasn't been properly implemented. if (tree.meth.getVisageTag() != VisageTag.SELECT && tree.meth.getVisageTag() != VisageTag.IDENT && ! rs.argumentsAcceptable(argtypes, mtype.getParameterTypes(), true, false, Warner.noWarnings)) log.error(tree, MsgSym.MESSAGE_VISAGE_CANNOT_APPLY_FUNCTION, mtype.getParameterTypes(), argtypes); // Check that value of resulting type is admissible in the // current context. Also, capture the return type result = check(tree, capture(restype), VAL, pkind, pt, pSequenceness); } else { log.error(tree, MsgSym.MESSAGE_VISAGE_NOT_A_FUNC, mtype, typeargtypes, Type.toString(argtypes)); tree.type = pt; result = pt; } Symbol msym = VisageTreeInfo.symbol(tree.meth); // We can add more methods here that we want to warn about. // If it becomes too hairy, it should be moved into a separate method, // and perhaps be table-driven. FIXME. if (msym != null && msym.owner instanceof ClassSymbol && ((ClassSymbol) msym.owner).fullname == defs.cJavaLangThreadName && msym.name == defs.start_ThreadMethodName) { log.warning(tree.pos(), MsgSym.MESSAGE_VISAGE_EXPLICIT_THREAD); } if (msym!=null && msym.owner!=null && msym.owner.type!=null && msym.owner.type.tsym == syms.visage_PointerType.tsym && methName == defs.make_PointerMethodName && argcount == 1) { msym.flags_field |= VisageFlags.FUNC_POINTER_MAKE; for (List<VisageExpression> l = tree.args; l.nonEmpty(); l = l.tail, i++) { VisageExpression arg = l.head; Symbol asym = VisageTreeInfo.symbol(arg); if (asym == null || !(asym.type instanceof ErrorType)) { if (asym == null || !(asym instanceof VisageVarSymbol) || (arg.getVisageTag() != VisageTag.IDENT && arg.getVisageTag() != VisageTag.SELECT) || asym.owner == null || (asym.owner.kind != TYP && !asym.isLocal())) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_APPLIED_TO_INSTANCE_VAR, methName); } } } } if (msym!=null && msym.owner!=null && msym.owner.type!=null && (msym.owner.type.tsym == syms.visage_AutoImportRuntimeType.tsym || msym.owner.type.tsym == syms.visage_RuntimeType.tsym) && (methName == defs.isInitialized_MethodName || methName == defs.isReadOnly_MethodName)) { msym.flags_field |= VisageFlags.FUNC_IS_BUILTINS_SYNTH; for (List<VisageExpression> l = tree.args; l.nonEmpty(); l = l.tail, i++) { VisageExpression arg = l.head; Symbol asym = VisageTreeInfo.symbol(arg); if (asym == null || !(asym.type instanceof ErrorType)) { if (asym == null || !(asym instanceof VisageVarSymbol) || (arg.getVisageTag() != VisageTag.IDENT && arg.getVisageTag() != VisageTag.SELECT) || (asym.flags() & VisageFlags.IS_DEF) != 0 || asym.owner == null || asym.owner.kind != TYP) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_APPLIED_TO_INSTANCE_VAR, methName); } else { // check that we have write access // unless it is a public-init or public-read var, that was already handled // the regular access check if ((asym.flags() & (VisageFlags.PUBLIC_INIT | VisageFlags.PUBLIC_READ)) != 0) { Type site; VisageTree base; switch (arg.getVisageTag()) { case IDENT: base = null; site = env.enclClass.sym.type; break; case SELECT: base = ((VisageSelect)arg).selected; site = base.type; break; default: throw new AssertionError(); // see above, should not occur } chk.checkAssignable(tree.pos(), (VisageVarSymbol) asym, base, site, env, WriteKind.VAR_QUERY); } } } } } chk.validate(tree.typeargs); } //@Override public void visitAssignop(VisageAssignOp tree) { // Attribute arguments. Type owntype = attribTree(tree.lhs, env, VAR, Type.noType); Type operand = attribExpr(tree.rhs, env); // Fix types of numeric arguments with non -specified type. Symbol lhsSym = VisageTreeInfo.symbol(tree.lhs); if (lhsSym != null && (lhsSym.type == null || lhsSym.type == Type.noType || lhsSym.type == syms.visage_AnyType)) { VisageVar lhsVarTree = varSymToTree.get(lhsSym); owntype = setBinaryTypes(tree.getVisageTag(), tree.lhs, lhsVarTree, lhsSym.type, lhsSym); } Symbol rhsSym = VisageTreeInfo.symbol(tree.rhs); if (rhsSym != null && (rhsSym.type == null || rhsSym.type == Type.noType || rhsSym.type == syms.visage_AnyType)) { VisageVar rhsVarTree = varSymToTree.get(rhsSym); operand = setBinaryTypes(tree.getVisageTag(), tree.rhs, rhsVarTree, rhsSym.type, rhsSym); } // Find operator. Symbol operator = tree.operator = attribBinop( tree.pos(), tree.getNormalOperatorVisageTag(), owntype, operand, env); if (operator.kind == MTH) { if (operator instanceof OperatorSymbol) { chk.checkOperator(tree.pos(), (OperatorSymbol)operator, tree.getVisageTag(), owntype, operand); } if (types.isSameType(operator.type.getReturnType(), syms.stringType)) { // String assignment; make sure the lhs is a string chk.checkType(tree.lhs.pos(), owntype, syms.stringType, Sequenceness.DISALLOWED); } else { chk.checkDivZero(tree.rhs.pos(), operator, operand); chk.checkCastable(tree.rhs.pos(), operator.type.getReturnType(), owntype); } } result = check(tree, operator.type.getReturnType(), VAL, pkind, owntype, pSequenceness); if (lhsSym != null && tree.rhs != null) { VisageVar lhsVar = varSymToTree.get(lhsSym); if (lhsVar != null && (lhsVar.getVisageType() instanceof VisageTypeUnknown)) { if ((lhsVar.type == null || lhsVar.type == syms.visage_AnyType)) { if (tree.rhs.type != null && lhsVar.type != tree.rhs.type) { lhsVar.type = lhsSym.type = tree.rhs.type; VisageExpression jcExpr = visagemake.at(tree.pos()).Ident(lhsSym); lhsVar.setVisageType(visagemake.at(tree.pos()).TypeClass(jcExpr, lhsVar.getVisageType().getCardinality())); } } } } } //@Override public void visitUnary(VisageUnary tree) { switch (tree.getVisageTag()) { case SIZEOF: { attribExpr(tree.arg, env); result = check(tree, syms.visage_IntegerType, VAL, pkind, pt, pSequenceness); return; } case REVERSE: { Type argtype = chk.checkNonVoid(tree.arg.pos(), attribExpr(tree.arg, env)); // result type is argument type, unless this is a singleton, then convert to a sequence Type owntype = (argtype.tag == ERROR || types.isSequence(argtype))? argtype : types.sequenceType(argtype); result = check(tree, owntype, VAL, pkind, pt, Sequenceness.REQUIRED); return; } } boolean isIncDec = tree.getVisageTag().isIncDec(); Type argtype; if (isIncDec) { // Attribute arguments. argtype = attribTree(tree.arg, env, VAR, Type.noType); } else { argtype = chk.checkNonVoid(tree.arg.pos(), attribExpr(tree.arg, env)); } //TODO: redundant now, but if we want to deferentiate error for increment/decremenet // from assignment, this code may be useful /*** if (isIncDec) { Symbol argSym = VisageTreeInfo.symbol(tree.arg); if (argSym == null) { log.error(tree, MsgSym.MESSAGE_VISAGE_INVALID_ASSIGNMENT); return; } if ((argSym.flags() & VisageFlags.IS_DEF) != 0L) { log.error(tree, MsgSym.MESSAGE_VISAGE_CANNOT_ASSIGN_TO_DEF, argSym); return; } if ((argSym.flags() & Flags.PARAMETER) != 0L) { log.error(tree, MsgSym.MESSAGE_VISAGE_CANNOT_ASSIGN_TO_PARAMETER, argSym); return; } } ***/ Symbol sym = rs.resolveUnaryOperator(tree.pos(), tree.getVisageTag(), env, types.unboxedTypeOrType(argtype)); Type owntype = syms.errType; if (sym instanceof OperatorSymbol) { // Find operator. Symbol operator = tree.operator = sym; if (operator.kind == MTH) { owntype = isIncDec ? tree.arg.type : operator.type.getReturnType(); } } else { owntype = sym.type.getReturnType(); } result = check(tree, owntype, VAL, pkind, pt, pSequenceness); } private Type setBinaryTypes(VisageTag opcode, VisageExpression tree, VisageVar var, Type type, Symbol treeSym) { Type newType = type; VisageExpression jcExpression = null; // boolean type if (opcode == VisageTag.OR || opcode == VisageTag.AND) { newType = syms.visage_BooleanType; jcExpression = visagemake.at(tree.pos()).Ident(syms.visage_BooleanType.tsym); } // Integer type else if (opcode == VisageTag.MOD) { newType = syms.visage_IntegerType; jcExpression = visagemake.at(tree.pos()).Ident(syms.visage_IntegerType.tsym); } // Number type else if (opcode == VisageTag.LT || opcode == VisageTag.GT || opcode == VisageTag.LE || opcode == VisageTag.GE || opcode == VisageTag.PLUS || opcode == VisageTag.MINUS || opcode == VisageTag.MUL || opcode == VisageTag.DIV || opcode == VisageTag.PLUS_ASG || opcode == VisageTag.MINUS_ASG || opcode == VisageTag.MUL_ASG || opcode == VisageTag.DIV_ASG) { newType = syms.visage_DoubleType; jcExpression = visagemake.at(tree.pos()).Ident(syms.visage_DoubleType.tsym); } else return newType; // tree is not null here tree.setType(newType); treeSym.type = newType; if (var != null) { var.setType(newType); VisageType visageType = visagemake.at(tree.pos()).TypeClass(jcExpression, Cardinality.SINGLETON); visageType.type = newType; var.setVisageType(visageType); var.sym.type = newType; } return newType; } public Symbol attribBinop(DiagnosticPosition pos, VisageTag tag, Type left, Type right, VisageEnv<VisageAttrContext> env) { boolean isEq = tag == VisageTag.EQ || tag == VisageTag.NE; //comparson operators == and != should work in the sequence vs. non-sequence //case - resolution of comparison operators works over non-sequence types Type leftUnboxed = (isEq && types.isSequence(left)) ? types.elementType(left) : types.unboxedTypeOrType(left); Type rightUnboxed = (isEq && types.isSequence(right)) ? types.elementType(right) : types.unboxedTypeOrType(right); return rs.resolveBinaryOperator(pos, tag, env, leftUnboxed, rightUnboxed); } //@Override public void visitBinary(VisageBinary tree) { // Attribute arguments. Type left = chk.checkNonVoid(tree.lhs.pos(), attribExpr(tree.lhs, env)); Type right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, env)); if (left == syms.visage_UnspecifiedType) { left = setEffectiveExpressionType(tree.lhs, newTypeFromType(getEffectiveExpressionType(right))); } else if (right == syms.visage_UnspecifiedType) { right = setEffectiveExpressionType(tree.rhs, newTypeFromType(getEffectiveExpressionType(left))); } // Fix types of numeric arguments with non -specified type. boolean lhsSet = false; // If an operand is untyped AND it's a var or attribute, constrain the // operand based on the operator. Rather a special-case kludge. Symbol lhsSym = VisageTreeInfo.symbol(tree.lhs); if (lhsSym != null && (lhsSym.type == null || lhsSym.type == Type.noType || lhsSym.type == syms.visage_AnyType)) { VisageVar lhsVarTree = varSymToTree.get(lhsSym); left = setBinaryTypes(tree.getVisageTag(), tree.lhs, lhsVarTree, lhsSym.type, lhsSym); lhsSet = true; } Symbol rhsSym = VisageTreeInfo.symbol(tree.rhs); if (rhsSym != null && (rhsSym.type == null || rhsSym.type == Type.noType || rhsSym.type == syms.visage_AnyType) || (lhsSet && lhsSym == rhsSym)) { VisageVar rhsVarTree = varSymToTree.get(rhsSym); right = setBinaryTypes(tree.getVisageTag(), tree.rhs, rhsVarTree, rhsSym.type, rhsSym); } Symbol sym = attribBinop(tree.pos(), tree.getVisageTag(), left, right, env); Type owntype = syms.errType; if (sym instanceof OperatorSymbol) { // Find operator. Symbol operator = tree.operator = sym; if (operator.kind == MTH) { owntype = operator.type.getReturnType(); int opc = chk.checkOperator(tree.lhs.pos(), (OperatorSymbol)operator, tree.getVisageTag(), left, right); // If both arguments are constants, fold them. if (left.constValue() != null && right.constValue() != null) { Type ctype = cfolder.fold2(opc, left, right); if (ctype != null) { owntype = cfolder.coerce(ctype, owntype); // Remove constant types from arguments to // conserve space. The parser will fold concatenations // of string literals; the code here also // gets rid of intermediate results when some of the // operands are constant identifiers. if (tree.lhs.type.tsym == syms.stringType.tsym) { tree.lhs.type = syms.stringType; } if (tree.rhs.type.tsym == syms.stringType.tsym) { tree.rhs.type = syms.stringType; } } } // Check that operands a, b of a binary reference comparison // ==, != are castable to each other (either a castable to b // or b castable to a) if ((opc == ByteCodes.if_acmpeq || opc == ByteCodes.if_acmpne)) { if (!types.isCastable(left, right, Warner.noWarnings) && !types.isCastable(right, left, Warner.noWarnings)) { log.error(tree.pos(), MsgSym.MESSAGE_INCOMPARABLE_TYPES, types.toVisageString(left), types.toVisageString(right)); } } chk.checkDivZero(tree.rhs.pos(), operator, right); } } else { owntype = sym.type.getReturnType(); } result = check(tree, owntype, VAL, pkind, pt, pSequenceness); if (tree.getVisageTag() == VisageTag.PLUS && owntype == syms.stringType) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_STRING_CONCATENATION, expressionToString(tree)); } } //where private String expressionToString(VisageExpression expr) { if (expr.type == syms.stringType) { if (expr.getVisageTag() == VisageTag.LITERAL) { return (String) (((VisageLiteral) expr).getValue()); } else if (expr.getVisageTag() == VisageTag.PLUS) { VisageBinary plus = (VisageBinary) expr; return expressionToString(plus.lhs) + expressionToString(plus.rhs); } } return "{" + expr.toString() + "}"; } boolean isPrimitiveOrBoxed(Type pt, int tag) { return pt.tag == tag || (pt.tsym instanceof ClassSymbol && ((ClassSymbol) pt.tsym).fullname == syms.boxedName[tag]); } //@Override public void visitLiteral(VisageLiteral tree) { Type expected = types.elementTypeOrType(pt); if (tree.value instanceof Double) { double dvalue = ((Double) tree.value).doubleValue(); double dabs = Math.abs(dvalue); boolean fitsInFloat = Double.isInfinite(dvalue) || dvalue == 0.0 || (dabs <= Float.MAX_VALUE && dabs >= Float.MIN_VALUE); if (isPrimitiveOrBoxed(expected, DOUBLE) || (expected.tag == UNKNOWN && !fitsInFloat)) { tree.typetag = TypeTags.DOUBLE; } else { if (isPrimitiveOrBoxed(expected, FLOAT) && !fitsInFloat) { log.error(tree, MsgSym.MESSAGE_VISAGE_LITERAL_OUT_OF_RANGE, "Number", tree.value.toString()); } tree.typetag = TypeTags.FLOAT; tree.value = Float.valueOf((float) dvalue); } } else if ((tree.value instanceof Integer || tree.value instanceof Long) && tree.typetag != TypeTags.BOOLEAN) { long lvalue = ((Number) tree.value).longValue(); if (isPrimitiveOrBoxed(expected, BYTE)) { if (lvalue != (byte) lvalue) { log.error(tree, MsgSym.MESSAGE_VISAGE_LITERAL_OUT_OF_RANGE, "Byte", tree.value.toString()); } tree.typetag = TypeTags.BYTE; tree.value = Byte.valueOf((byte) lvalue); } else if (isPrimitiveOrBoxed(expected, SHORT)) { if (lvalue != (short) lvalue) { log.error(tree, MsgSym.MESSAGE_VISAGE_LITERAL_OUT_OF_RANGE, "Short", tree.value.toString()); } tree.typetag = TypeTags.SHORT; tree.value = Short.valueOf((short) lvalue); } else if (isPrimitiveOrBoxed(expected, CHAR) && lvalue == (char) lvalue) { tree.typetag = TypeTags.CHAR; } else if (isPrimitiveOrBoxed(expected, FLOAT)) { tree.typetag = TypeTags.FLOAT; tree.value = Float.valueOf(lvalue); } else if (isPrimitiveOrBoxed(expected, DOUBLE)) { tree.typetag = TypeTags.DOUBLE; tree.value = Double.valueOf(lvalue); } else if (isPrimitiveOrBoxed(expected, INT) || tree.typetag == TypeTags.INT) { if (tree.typetag == TypeTags.LONG) { log.error(tree, MsgSym.MESSAGE_VISAGE_LITERAL_OUT_OF_RANGE, "Integer", tree.value.toString()); } tree.typetag = TypeTags.INT; if (! (tree.value instanceof Integer)) tree.value = Integer.valueOf((int) lvalue); } else { tree.typetag = TypeTags.LONG; if (! (tree.value instanceof Long)) tree.value = Long.valueOf(lvalue); } } result = check( tree, litType(tree.typetag, pt), VAL, pkind, pt, pSequenceness); } //where /** Return the type of a literal with given type tag. */ private Type litType(int tag, Type pt) { switch (tag) { case TypeTags.CLASS: return syms.stringType; case TypeTags.BOT: return types.isSequence(pt) && !types.isNullable(types.elementType(pt)) ? pt : syms.botType; default: return syms.typeOfTag[tag]; } } //@Override public void visitErroneous(VisageErroneous tree) { // if (tree.getErrorTrees() != null) // for (VisageTree err : tree.getErrorTrees()) // attribTree(err, env, ERR, pt); result = tree.type = syms.errType; } /** Main method: attribute class definition associated with given class symbol. * reporting completion failures at the given position. * @param pos The source position at which completion errors are to be * reported. * @param c The class symbol whose definition will be attributed. */ public void attribClass(DiagnosticPosition pos, VisageClassDeclaration tree, ClassSymbol c) { try { annotate.flush(); attribClass(tree, c); } catch (CompletionFailure ex) { chk.completionError(pos, ex); } } /** Attribute class definition associated with given class symbol. * @param c The class symbol whose definition will be attributed. */ void attribClass(VisageClassDeclaration tree, ClassSymbol c) throws CompletionFailure { if (c.type.tag == ERROR) return; // Check for cycles in the inheritance graph, which can arise from // ill-formed class files. chk.checkNonCyclic(null, c.type); if (tree != null) { attribSupertypes(tree, c); } // The previous operations might have attributed the current class // if there was a cycle. So we test first whether the class is still // UNATTRIBUTED. if ((c.flags_field & UNATTRIBUTED) != 0) { c.flags_field &= ~UNATTRIBUTED; // Get environment current at the point of class definition. VisageEnv<VisageAttrContext> localEnv = enter.typeEnvs.get(c); // The info.lint field in the envs stored in enter.typeEnvs is deliberately uninitialized, // because the annotations were not available at the time the env was created. Therefore, // we look up the environment chain for the first enclosing environment for which the // lint value is set. Typically, this is the parent env, but might be further if there // are any envs created as a result of TypeParameter nodes. VisageEnv<VisageAttrContext> lintEnv = localEnv; while (lintEnv.info.lint == null) lintEnv = lintEnv.next; // Having found the enclosing lint value, we can initialize the lint value for this class localEnv.info.lint = lintEnv.info.lint.augment(c.attributes_field, c.flags()); Lint prevLint = chk.setLint(localEnv.info.lint); JavaFileObject prev = log.useSource(c.sourcefile); try { attribClassBody(localEnv, c); } finally { log.useSource(prev); chk.setLint(prevLint); } } } /** Clones a type without copiyng constant values * @param t the type that needs to be cloned. * @return the cloned type with no cloned constants. */ public Type newTypeFromType(Type t) { if (t == null) return null; switch (t.tag) { case BYTE: return syms.byteType; case CHAR: return syms.charType; case SHORT: return syms.shortType; case INT: return syms.intType; case LONG: return syms.longType; case FLOAT: return syms.floatType; case DOUBLE: return syms.doubleType; case BOOLEAN: return syms.booleanType; case VOID: return syms.voidType; default: return t; } } /** * Gets the effective type of a type. If MethodType - the return type, * otherwise the passed in type. */ private Type getEffectiveExpressionType(Type type) { if (type.tag == TypeTags.METHOD) { return type.getReturnType(); } return type; } /** * Sets the effective type of an expression. If MethodType - the return type, * otherwise the whole type of the expression is set. */ private Type setEffectiveExpressionType(VisageExpression expression, Type type) { if (expression.type.tag == TypeTags.METHOD) { ((MethodType)expression.type).restype = type; } else { expression.type = type; } return expression.type; } // Begin Visage trees //@Override public void visitClassDeclaration(VisageClassDeclaration tree) { // Local classes have not been entered yet, so we need to do it now: if ((env.info.scope.owner.kind & (VAR | MTH)) != 0) enter.classEnter(tree, env); ClassSymbol c = tree.sym; if (c == null) { // exit in case something drastic went wrong during enter. result = null; } else { // make sure class has been completed: c.complete(); attribSupertypes(tree, c); attribClass(tree.pos(), tree, c); result = tree.type = c.type; types.addVisageClass(c, tree); } result = syms.voidType; } private void attribSupertypes(VisageClassDeclaration tree, ClassSymbol c) { VisageClassSymbol visageClassSymbol = null; if (c instanceof VisageClassSymbol) { visageClassSymbol = (VisageClassSymbol)c; } Symbol javaSupertypeSymbol = null; for (VisageExpression superClass : tree.getSupertypes()) { Type supType = superClass.type == null ? attribType(superClass, env) : superClass.type; // java.lang.Enum may not be subclassed by a non-enum if (supType.tsym == syms.enumSym && ((c.flags_field & (Flags.ENUM|Flags.COMPOUND)) == 0)) log.error(superClass.pos(), MsgSym.MESSAGE_ENUM_NO_SUBCLASSING); // Enums may not be extended by source-level classes if (supType.tsym != null && ((supType.tsym.flags_field & Flags.ENUM) != 0) && ((c.flags_field & Flags.ENUM) == 0) && !target.compilerBootstrap(c)) { log.error(superClass.pos(), MsgSym.MESSAGE_ENUM_TYPES_NOT_EXTENSIBLE); } if (!supType.isInterface() && !types.isVisageClass(supType.tsym) && !types.isMixin(supType.tsym) && !supType.isPrimitive() && visageClassSymbol.type instanceof ClassType) { if (javaSupertypeSymbol == null) { javaSupertypeSymbol = supType.tsym; // Verify there is a non-parametric constructor. boolean hasNonParamCtor = true; // If there is no non-param constr we will create one later. for (Scope.Entry e1 = javaSupertypeSymbol.members().elems; e1 != null; e1 = e1.sibling) { Symbol s1 = e1.sym; if (s1 != null && s1.name == names.init && s1.kind == Kinds.MTH) { MethodType mtype = ((MethodSymbol)s1).type.asMethodType(); if (mtype != null && mtype.getParameterTypes().isEmpty()) { hasNonParamCtor = true; break; } else { hasNonParamCtor = false; } } } if (hasNonParamCtor) { ((ClassType)visageClassSymbol.type).supertype_field = supType; } else { log.error(superClass.pos(), MsgSym.MESSAGE_VISAGE_BASE_JAVA_CLASS_NON_PAPAR_CTOR, supType.tsym.name); } } else { // We are already extending one Java class. No more than one is allowed. Report an error. log.error(superClass.pos(), MsgSym.MESSAGE_VISAGE_ONLY_ONE_BASE_JAVA_CLASS_ALLOWED, supType.tsym.name); } } } } //@Override public void visitInitDefinition(VisageInitDefinition that) { Symbol symOwner = env.info.scope.owner; try { MethodType mt = new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), (TypeSymbol)symOwner); that.sym = new MethodSymbol(0L, defs.init_MethodSymbolName, mt, symOwner); env.info.scope.owner = that.sym; VisageEnv<VisageAttrContext> localEnv = env.dup(that); localEnv.outer = env; attribExpr(that.getBody(), localEnv); } finally { env.info.scope.owner = symOwner; } } public void visitPostInitDefinition(VisagePostInitDefinition that) { Symbol symOwner = env.info.scope.owner; try { MethodType mt = new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), (TypeSymbol)symOwner); that.sym = new MethodSymbol(0L, defs.postinit_MethodSymbolName, mt, symOwner); env.info.scope.owner = that.sym; VisageEnv<VisageAttrContext> localEnv = env.dup(that); localEnv.outer = env; attribExpr(that.getBody(), localEnv); } finally { env.info.scope.owner = symOwner; } } //@Override public void visitSequenceEmpty(VisageSequenceEmpty tree) { boolean isSeq = types.isSequence(pt); Type owntype = pt.tag == NONE || pt.tag == UNKNOWN ? syms.visage_EmptySequenceType : isSeq ? pt : types.sequenceType(pt); result = check(tree, owntype, VAL, pkind, Type.noType, pSequenceness); } //@Override public void visitSequenceRange(VisageSequenceRange tree) { Type lowerType = attribExpr(tree.getLower(), env); Type upperType = attribExpr(tree.getUpper(), env); Type stepType = tree.getStepOrNull() == null? syms.visage_IntegerType : attribExpr(tree.getStepOrNull(), env); boolean allInt = true; if (lowerType != syms.visage_IntegerType) { allInt = false; if (lowerType != syms.visage_FloatType && lowerType != syms.visage_DoubleType) { log.error(tree.getLower().pos(), MsgSym.MESSAGE_VISAGE_RANGE_START_INT_OR_NUMBER); } } if (upperType != syms.visage_IntegerType) { allInt = false; if (upperType != syms.visage_FloatType && upperType != syms.visage_DoubleType) { log.error(tree.getLower().pos(), MsgSym.MESSAGE_VISAGE_RANGE_END_INT_OR_NUMBER); } } if (stepType != syms.visage_IntegerType) { allInt = false; if (stepType != syms.visage_FloatType && stepType != syms.visage_DoubleType) { log.error(tree.getStepOrNull().pos(), MsgSym.MESSAGE_VISAGE_RANGE_STEP_INT_OR_NUMBER); } } if (tree.getLower().getVisageTag() == VisageTag.LITERAL && tree.getUpper().getVisageTag() == VisageTag.LITERAL && (tree.getStepOrNull() == null || tree.getStepOrNull().getVisageTag() == VisageTag.LITERAL)) { chk.warnEmptyRangeLiteral(tree.pos(), (VisageLiteral)tree.getLower(), (VisageLiteral)tree.getUpper(), (VisageLiteral)tree.getStepOrNull(), tree.isExclusive()); } Type owntype = types.sequenceType(allInt? syms.visage_IntegerType : syms.visage_FloatType); result = tree.type = check(tree, owntype, VAL, pkind, pt, pSequenceness); } //@Override public void visitSequenceExplicit(VisageSequenceExplicit tree) { Type elemType = null; boolean errorFound = false; for (VisageExpression expr : tree.getItems()) { Type itemType = attribTree(expr, env, VAL, pt, Sequenceness.PERMITTED); if (types.isSequence(itemType) || types.isArray(itemType)) { itemType = types.isSequence(itemType) ? types.elementType(itemType) : types.elemtype(itemType); } itemType = chk.checkNonVoid(expr, itemType); if (elemType == null || itemType.tag == NONE || itemType.tag == ERROR) { elemType = itemType; } else elemType = unionType(tree, itemType, elemType); errorFound |= elemType.isErroneous() || itemType.isErroneous(); } if (!errorFound && (pt.tag == NONE || pt == syms.visage_UnspecifiedType)) { Type typeToCheck = types.sequenceType(types.normalize(elemType)); for (VisageExpression expr : tree.getItems()) { attribTree(expr, env, VAL, typeToCheck, Sequenceness.PERMITTED); } } Type owntype = elemType.tag == ERROR ? elemType : types.sequenceType(elemType); result = check(tree, owntype, VAL, pkind, pt, pSequenceness); if (owntype == result && pt.tag != NONE && pt != syms.visage_UnspecifiedType && pt != syms.objectType) { result = tree.type = pt; } } //@Override public void visitSequenceSlice(VisageSequenceSlice tree) { VisageExpression seq = tree.getSequence(); // Attribute as a tree so we can check that target is assignable // when pkind is VAR // Type seqType = attribTree(seq, env, pkind, Type.noType, Sequenceness.REQUIRED); attribExpr(tree.getFirstIndex(), env, syms.visage_IntegerType); if (tree.getLastIndex() != null) { attribExpr(tree.getLastIndex(), env, syms.visage_IntegerType); } result = check(tree, seqType, VAR, pkind, pt, pSequenceness); } //@Override public void visitSequenceIndexed(VisageSequenceIndexed tree) { VisageExpression seq = tree.getSequence(); // Attribute as a tree so we can check that target is assignable // when pkind is VAR // Type seqType = attribTree(seq, env, pkind, Type.noType, Sequenceness.PERMITTED); attribExpr(tree.getIndex(), env, syms.visage_IntegerType); chk.checkSequenceOrArrayType(seq.pos(), seqType); Type owntype = seqType.tag == ARRAY ? types.elemtype(seqType) : types.elementType(seqType); result = check(tree, owntype, VAR, pkind, pt, pSequenceness); } //@Override public void visitSequenceInsert(VisageSequenceInsert tree) { VisageExpression seq = tree.getSequence(); Type seqType = attribTree(seq, env, VAR, Type.noType, Sequenceness.REQUIRED); attribExpr(tree.getElement(), env, seqType); if (tree.getPosition() != null) { attribExpr(tree.getPosition(), env, syms.visage_IntegerType); } result = syms.voidType; tree.type = result; } //@Override public void visitSequenceDelete(VisageSequenceDelete tree) { VisageExpression seq = tree.getSequence(); if (tree.getElement() == null) { Sequenceness sequenceness = (seq instanceof VisageSequenceIndexed) ? Sequenceness.DISALLOWED : Sequenceness.REQUIRED; int pkind = (seq instanceof VisageSequenceIndexed) || (seq instanceof VisageSequenceSlice) ? VAL : VAR; attribTree(seq, env, pkind, Type.noType, sequenceness); } else { Type seqType = attribTree(seq, env, VAR, Type.noType, Sequenceness.REQUIRED); attribExpr(tree.getElement(), env, chk.checkSequenceElementType(seq.pos(), seqType)); } result = syms.voidType; tree.type = result; } //@Override public void visitInvalidate(VisageInvalidate tree) { //the target expr should be a variable attribTree(tree.getVariable(), env, VAR, Type.noType); // Symbol varSym = VisageTreeInfo.symbol(tree.getVariable()); // if (varSym != null && // varSym.kind == VAR) { // //non-static/local vars can be overridden with a bound init - other cases // //are handled at runtime (exception) // if ((varSym.flags_field & VisageFlags.VARUSE_BOUND_DEFINITION) == 0 && // (varSym.isStatic() || varSym.owner.kind != TYP)) // log.error(tree.getVariable().pos(), MsgSym.MESSAGE_CANNOT_INVALIDATE_UNBOUND_VAR, varSym); // } result = tree.type = syms.voidType; } //@Override public void visitStringExpression(VisageStringExpression tree) { List<VisageExpression> parts = tree.getParts(); attribExpr(parts.head, env, syms.visage_StringType); parts = parts.tail; while (parts.nonEmpty()) { // First the format specifier: attribExpr(parts.head, env, syms.visage_StringType); parts = parts.tail; // Next the enclosed expression: chk.checkNonVoid(parts.head.pos(), attribExpr(parts.head, env, Type.noType)); parts = parts.tail; // Next the following string literal part: attribExpr(parts.head, env, syms.visage_StringType); parts = parts.tail; } result = check(tree, syms.visage_StringType, VAL, pkind, pt, pSequenceness); } //@Override public void visitObjectLiteralPart(VisageObjectLiteralPart that) { // Note that this method can be reached legitimately if visitErroneous is // called and the error nodes contain an objectLiteralPart. Hence this // just sets the result to errType. // result = syms.errType; } //@Override public void visitTypeAny(VisageTypeAny tree) { assert false : "MUST IMPLEMENT"; } //@Override public void visitTypeClass(VisageTypeClass tree) { VisageExpression classNameExpr = ((VisageTypeClass) tree).getClassName(); Type type = attribType(classNameExpr, env); Cardinality cardinality = tree.getCardinality(); if (cardinality != Cardinality.SINGLETON && type == syms.voidType) { log.error(tree, MsgSym.MESSAGE_VISAGE_VOID_SEQUENCE_NOT_ALLOWED); cardinality = Cardinality.SINGLETON; } type = sequenceType(type, cardinality); tree.type = type; result = type; } //@Override public void visitTypeFunctional(VisageTypeFunctional tree) { Type restype = attribType(tree.restype, env); if (restype == syms.unknownType) restype = syms.voidType; Type rtype = restype == syms.voidType ? syms.visage_java_lang_VoidType : new WildcardType(types.boxedTypeOrType(restype), BoundKind.EXTENDS, syms.boundClass); ListBuffer<Type> typarams = new ListBuffer<Type>(); ListBuffer<Type> argtypes = new ListBuffer<Type>(); typarams.append(rtype); int nargs = 0; for (VisageType param : (List<VisageType>)tree.params) { Type argtype = attribType(param, env); if (argtype == syms.visage_UnspecifiedType) argtype = syms.objectType; argtypes.append(argtype); Type ptype = types.boxedTypeOrType(argtype); ptype = new WildcardType(ptype, BoundKind.SUPER, syms.boundClass); typarams.append(ptype); nargs++; } MethodType mtype = new MethodType(argtypes.toList(), restype, null, syms.methodClass); if (nargs > VisageSymtab.MAX_FIXED_PARAM_LENGTH) { log.error(tree, MsgSym.MESSAGE_TOO_MANY_PARAMETERS); tree.type = result = syms.objectType; return; } FunctionType ftype = syms.makeFunctionType(typarams.toList(), mtype); Type type = sequenceType(ftype, tree.getCardinality()); tree.type = type; result = type; } //@Override public void visitTypeArray(VisageTypeArray tree) { //TODO: Do the right thing here Type etype = attribType(tree.getElementType(), env); Type type = new ArrayType(etype, syms.arrayClass); tree.type = type; result = type; } //@Override public void visitTypeUnknown(VisageTypeUnknown tree) { result = tree.type = syms.visage_UnspecifiedType; } Type sequenceType(Type elemType, Cardinality cardinality) { return cardinality == cardinality.ANY ? types.sequenceType(elemType) : elemType; } /** Determine type of identifier or select expression and check that * (1) the referenced symbol is not deprecated * (2) the symbol's type is safe (@see checkSafe) * (3) if symbol is a variable, check that its type and kind are * compatible with the prototype and protokind. * (4) if symbol is an instance field of a raw type, * which is being assigned to, issue an unchecked warning if its * type changes under erasure. * (5) if symbol is an instance method of a raw type, issue an * unchecked warning if its argument types change under erasure. * If checks succeed: * If symbol is a constant, return its constant type * else if symbol is a method, return its result type * otherwise return its type. * Otherwise return errType. * * @param tree The syntax tree representing the identifier * @param site If this is a select, the type of the selected * expression, otherwise the type of the current class. * @param sym The symbol representing the identifier. * @param env The current environment. * @param pkind The set of expected kinds. * @param pt The expected type. */ Type checkId(VisageTree tree, Type site, Symbol sym, VisageEnv<VisageAttrContext> env, int pkind, Type pt, Sequenceness pSequenceness, boolean useVarargs) { if (pt.isErroneous()) return syms.errType; Type owntype; // The computed type of this identifier occurrence. switch (sym.kind) { case TYP: // For types, the computed type equals the symbol's type, // except for two situations: owntype = sym.type; if (owntype.tag == CLASS) { Type ownOuter = owntype.getEnclosingType(); // (a) If the symbol's type is parameterized, erase it // because no type parameters were given. // We recover generic outer type later in visitTypeApply. if (owntype.tsym.type.getTypeArguments().nonEmpty()) { owntype = types.erasure(owntype); } // (b) If the symbol's type is an inner class, then // we have to interpret its outer type as a superclass // of the site type. Example: // // class Tree<A> { class Visitor { ... } } // class PointTree extends Tree<Point> { ... } // ...PointTree.Visitor... // // Then the type of the last expression above is // Tree<Point>.Visitor. else if (ownOuter.tag == CLASS && site != ownOuter) { Type normOuter = site; if (normOuter.tag == CLASS) normOuter = types.asEnclosingSuper(site, ownOuter.tsym); if (normOuter == null) // perhaps from an import normOuter = types.erasure(ownOuter); if (normOuter != ownOuter) owntype = new ClassType( normOuter, List.<Type>nil(), owntype.tsym); } } break; case VAR: VisageVarSymbol v = (VisageVarSymbol)sym; // Test (4): if symbol is an instance field of a raw type, // which is being assigned to, issue an unchecked warning if // its type changes under erasure. if (allowGenerics && pkind == VAR && v.isMember() && (v.flags() & STATIC) == 0 && (site.tag == CLASS || site.tag == TYPEVAR)) { Type s = types.asOuterSuper(site, v.owner); if (s != null && s.isRaw() && !types.isSameType(v.type, v.erasure(types))) { chk.warnUnchecked(tree.pos(), MsgSym.MESSAGE_UNCHECKED_ASSIGN_TO_VAR, v, s); } } // The computed type of a variable is the type of the // variable symbol, taken as a member of the site type. owntype = (sym.owner.kind == TYP && sym.name != names._this && sym.name != names._super) ? types.memberType(site, sym) : sym.type; if (env.info.tvars.nonEmpty()) { Type owntype1 = new ForAll(env.info.tvars, owntype); for (List<Type> l = env.info.tvars; l.nonEmpty(); l = l.tail) if (!owntype.contains(l.head)) { log.error(tree.pos(), MsgSym.MESSAGE_UNDETERMINDED_TYPE, owntype1); owntype1 = syms.errType; } owntype = owntype1; } // If the variable is a constant, record constant value in // computed type. //if (v.getConstValue() != null && isStaticReference(tree)) // owntype = owntype.constType(v.getConstValue()); if (pkind == VAL) { owntype = capture(owntype); // capture "names as expressions" } break; case MTH: { owntype = types.memberType(types.erasure(site), sym); // This is probably wrong now that we have function expressions. // Instead, we should checkMethod in visitFunctionInvocation. // In that case we should also handle FunctionType. FIXME. if (pt instanceof MethodType || pt instanceof ForAll) { VisageFunctionInvocation app = (VisageFunctionInvocation)env.tree; owntype = checkMethod(site, sym, env, app.args, pt.getParameterTypes(), pt.getTypeArguments(), env.info.varArgs); } break; } case PCK: case ERR: owntype = sym.type; break; default: throw new AssertionError("unexpected kind: " + sym.kind + " in tree " + tree); } // Test (1): emit a `deprecation' warning if symbol is deprecated. // (for constructors, the error was given when the constructor was // resolved) if (sym.name != names.init && (sym.flags() & DEPRECATED) != 0 && (env.info.scope.owner.flags() & DEPRECATED) == 0 && sym.outermostClass() != env.info.scope.owner.outermostClass()) chk.warnDeprecated(tree.pos(), sym); if (warnOnUsePackages != null && ElementKind.PACKAGE.equals(sym.getKind())) { for (String pkg : warnOnUsePackages) { if (sym.toString().startsWith(pkg)) { chk.warnWarnOnUsePackage(tree.pos(), sym); } } } if ((sym.flags() & PROPRIETARY) != 0) log.strictWarning(tree.pos(), MsgSym.MESSAGE_SUN_PROPRIETARY, sym); // Test (3): if symbol is a variable, check that its type and // kind are compatible with the prototype and protokind. return check(tree, owntype, sym.kind, pkind, pt, pSequenceness); } public boolean isClassOrFuncDef(VisageEnv<VisageAttrContext> env, boolean discardRun) { return isFunctionDef(env, discardRun) || env.tree.getVisageTag() == VisageTag.FUNCTIONEXPRESSION || env.tree.getVisageTag() == VisageTag.CLASS_DEF || env.tree.getVisageTag() == VisageTag.ON_REPLACE || env.tree.getVisageTag() == VisageTag.KEYFRAME_LITERAL || env.tree.getVisageTag() == VisageTag.INIT_DEF || env.tree.getVisageTag() == VisageTag.POSTINIT_DEF; } //where private boolean isFunctionDef(VisageEnv<VisageAttrContext> env, boolean discardRun) { return env.tree.getVisageTag() == VisageTag.FUNCTION_DEF && (!discardRun || !(((VisageFunctionDefinition)env.tree).name.equals(syms.runMethodName))); } Warner noteWarner = new Warner(); /** * Check that method arguments conform to its instantation. **/ public Type checkMethod(Type site, Symbol sym, VisageEnv<VisageAttrContext> env, final List<VisageExpression> argtrees, List<Type> argtypes, List<Type> typeargtypes, boolean useVarargs) { // Test (5): if symbol is an instance method of a raw type, issue // an unchecked warning if its argument types change under erasure. if (allowGenerics && (sym.flags() & STATIC) == 0 && (site.tag == CLASS || site.tag == TYPEVAR)) { Type s = types.asOuterSuper(site, sym.owner); if (s != null && s.isRaw() && !types.isSameTypes(sym.type.getParameterTypes(), sym.erasure(types).getParameterTypes())) { chk.warnUnchecked(env.tree.pos(), MsgSym.MESSAGE_UNCHECKED_CALL_MBR_OF_RAW_TYPE, sym, s); } } // Compute the identifier's instantiated type. // For methods, we need to compute the instance type by // Resolve.instantiate from the symbol's type as well as // any type arguments and value arguments. noteWarner.warned = false; Type owntype = rs.instantiate(env, site, sym, argtypes, typeargtypes, true, useVarargs, noteWarner); boolean warned = noteWarner.warned; // If this fails, something went wrong; we should not have // found the identifier in the first place. if (owntype == null) { if (!pt.isErroneous()) log.error(env.tree.pos(), MsgSym.MESSAGE_INTERNAL_ERROR_CANNOT_INSTANTIATE, sym, site, Type.toString(pt.getParameterTypes())); owntype = syms.errType; } else { // System.out.println("call : " + env.tree); // System.out.println("method : " + owntype); // System.out.println("actuals: " + argtypes); List<Type> formals = owntype.getParameterTypes(); Type last = useVarargs ? formals.last() : null; if (sym.name==names.init && sym.owner == syms.enumSym) formals = formals.tail.tail; List<VisageExpression> args = argtrees; while (formals.head != last) { VisageTree arg = args.head; Warner warn = chk.convertWarner(arg.pos(), arg.type, formals.head); assertConvertible(arg, arg.type, formals.head, warn); warned |= warn.warned; args = args.tail; formals = formals.tail; } if (useVarargs) { Type varArg = types.elemtype(last); while (args.tail != null) { VisageTree arg = args.head; Warner warn = chk.convertWarner(arg.pos(), arg.type, varArg); assertConvertible(arg, arg.type, varArg, warn); warned |= warn.warned; args = args.tail; } } else if ((sym.flags() & VARARGS) != 0 && allowVarargs) { // non-varargs call to varargs method Type varParam = owntype.getParameterTypes().last(); Type lastArg = argtypes.last(); if (types.isSubtypeUnchecked(lastArg, types.elemtype(varParam)) && !types.isSameType(types.erasure(varParam), types.erasure(lastArg))) log.warning(argtrees.last().pos(), MsgSym.MESSAGE_INEXACT_NON_VARARGS_CALL, types.elemtype(varParam), varParam); } if (warned && sym.type.tag == FORALL) { String typeargs = ""; if (typeargtypes != null && typeargtypes.nonEmpty()) { typeargs = "<" + Type.toString(typeargtypes) + ">"; } chk.warnUnchecked(env.tree.pos(), MsgSym.MESSAGE_UNCHECKED_METH_INVOCATION_APPLIED, sym, sym.location(), typeargs, Type.toString(argtypes)); owntype = new MethodType(owntype.getParameterTypes(), types.erasure(owntype.getReturnType()), owntype.getThrownTypes(), syms.methodClass); } if (useVarargs) { VisageTree tree = env.tree; Type argtype = owntype.getParameterTypes().last(); if (!types.isReifiable(argtype)) chk.warnUnchecked(env.tree.pos(), MsgSym.MESSAGE_UNCHECKED_GENERIC_ARRAY_CREATION, argtype); Type elemtype = types.elemtype(argtype); switch (tree.getVisageTag()) { case APPLY: ((VisageFunctionInvocation) tree).varargsElement = elemtype; break; default: throw new AssertionError(""+tree); } } } return owntype; } private void assertConvertible(VisageTree tree, Type actual, Type formal, Warner warn) { if (types.isConvertible(actual, formal, warn)) return; if (formal.isCompound() && types.isSubtype(actual, types.supertype(formal)) && types.isSubtypeUnchecked(actual, types.interfaces(formal), warn)) return; if (false) { // TODO: make assertConvertible work chk.typeError(tree.pos(), JCDiagnostic.fragment(MsgSym.MESSAGE_INCOMPATIBLE_TYPES), actual, formal); throw new AssertionError("Tree: " + tree + " actual:" + actual + " formal: " + formal); } } //@Override public void visitImport(VisageImport tree) { // nothing to do } /** Finish the attribution of a class. */ public void attribClassBody(VisageEnv<VisageAttrContext> env, ClassSymbol c) { VisageClassDeclaration tree = (VisageClassDeclaration)env.tree; assert c == tree.sym; // Validate annotations //chk.validateAnnotations(tree.mods.annotations, c); // Validate type parameters, supertype and interfaces. //attribBounds(tree.getEmptyTypeParameters()); //chk.validateTypeParams(tree.getEmptyTypeParameters()); chk.validate(tree.getSupertypes()); // Check that class does not import the same parameterized interface // with two different argument lists. chk.checkClassBounds(tree.pos(), c.type); tree.type = c.type; // Check that a generic class doesn't extend Throwable if (!c.type.allparams().isEmpty() && types.isSubtype(c.type, syms.throwableType)) log.error(tree.getExtending().head.pos(), MsgSym.MESSAGE_GENERIC_THROWABLE); for (List<VisageTree> l = tree.getMembers(); l.nonEmpty(); l = l.tail) { // Attribute declaration attribDecl(l.head, env); // Check that declarations in inner classes are not static (JLS 8.1.2) // Make an exception for static constants. // Visage allows that. // if (c.owner.kind != PCK && // ((c.flags() & STATIC) == 0 || c.name == names.empty) && // (VisageTreeInfo.flags(l.head) & (STATIC | INTERFACE)) != 0) { // Symbol sym = null; // if (l.head.getVisageTag() == VisageTag.VARDEF) sym = ((JCVariableDecl) l.head).sym; // if (sym == null || // sym.kind != VAR || // ((VisageVarSymbol) sym).getConstValue() == null) // log.error(l.head.pos(), "icls.cant.have.static.decl"); // } } // If this is a non-abstract class, check that it has no abstract // methods or unimplemented methods of an implemented interface. if ((c.flags() & (ABSTRACT | INTERFACE | VisageFlags.MIXIN)) == 0) { if (!relax) chk.checkAllDefined(tree.pos(), c); } // Make sure there is only one real base class. Others may be mixins. chk.checkOneBaseClass(tree); // If the class is a mixin if ((c.flags() & (VisageFlags.MIXIN)) != 0) { // Check that the mixin is only a pure mixin class. chk.checkPureMixinClass(tree.pos(), c); // Check that only it only extends mixins and interfaces. chk.checkOnlyMixinsAndInterfaces(tree); } else { // Check to make sure that mixins don't cause any conflicts. chk.checkMixinConflicts(tree); } // Check that all extended classes and interfaces // are compatible (i.e. no two define methods with same arguments // yet different return types). (JLS 8.4.6.3) chk.checkCompatibleSupertypes(tree.pos(), c.type); // Check that all methods which implement some // method conform to the method they implement. chk.checkImplementations(tree); if (tree.isScriptClass) { chk.checkForwardReferences(tree); } Scope enclScope = VisageEnter.enterScope(env); for (List<VisageTree> l = tree.getMembers(); l.nonEmpty(); l = l.tail) { if (l.head instanceof VisageFunctionDefinition) chk.checkUnique(l.head.pos(), ((VisageFunctionDefinition) l.head).sym, enclScope); } // Check for proper use of serialVersionUID if (env.info.lint.isEnabled(Lint.LintCategory.SERIAL) && isSerializable(c) && (c.flags() & Flags.ENUM) == 0 && (c.flags() & ABSTRACT | VisageFlags.MIXIN) == 0) { checkSerialVersionUID(tree, c); } } // where /** check if a class is a subtype of Serializable, if that is available. */ private boolean isSerializable(ClassSymbol c) { try { syms.serializableType.complete(); } catch (CompletionFailure e) { return false; } return types.isSubtype(c.type, syms.serializableType); } /** Check that an appropriate serialVersionUID member is defined. */ private void checkSerialVersionUID(VisageClassDeclaration tree, ClassSymbol c) { // check for presence of serialVersionUID Scope.Entry e = c.members().lookup(names.serialVersionUID); while (e.scope != null && e.sym.kind != VAR) e = e.next(); if (e.scope == null) { log.warning(tree.pos(), MsgSym.MESSAGE_MISSING_SVUID, c); return; } // check that it is static final VisageVarSymbol svuid = (VisageVarSymbol)e.sym; if ((svuid.flags() & (STATIC | FINAL)) != (STATIC | FINAL)) log.warning(VisageTreeInfo.diagnosticPositionFor(svuid, tree), MsgSym.MESSAGE_IMPROPER_SVUID, c); // check that it is long else if (svuid.type.tag != TypeTags.LONG) log.warning(VisageTreeInfo.diagnosticPositionFor(svuid, tree), MsgSym.MESSAGE_LONG_SVUID, c); // check constant else if (svuid.getConstValue() == null) log.warning(VisageTreeInfo.diagnosticPositionFor(svuid, tree), MsgSym.MESSAGE_CONSTANT_SVUID, c); } private Type capture(Type type) { Type ctype = types.capture(type); if (type instanceof FunctionType) ctype = new FunctionType((FunctionType) type); return ctype; } public void clearCaches() { varSymToTree = null; methodSymToTree = null; } private void fixOverride(VisageFunctionDefinition tree, MethodSymbol m, boolean fixFlags) { ClassSymbol origin = (ClassSymbol) m.owner; if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) { if (m.overrides(syms.enumFinalFinalize, origin, types, false)) { log.error(tree.pos(), MsgSym.MESSAGE_ENUM_NO_FINALIZE); return; } } for (Type t : types.supertypesClosure(origin.type)) { if (t.tag == CLASS) { TypeSymbol c = t.tsym; Scope.Entry e = c.members().lookup(m.name); while (e.scope != null) { e.sym.complete(); if (m.overrides(e.sym, origin, types, false)) { //if other has an uninferred return type we should report an error if (e.sym.type.asMethodType().restype == syms.visage_UnspecifiedType) { log.note(tree, MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_FUN_DECL, e.sym.name); log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_TYPE_INFER_CYCLE_VAR_REF, e.sym.name); //set a dummy return type for other so that visagec is happy ((MethodType)e.sym.type).restype = syms.errType; break; } else if (fixOverride(tree, m, (MethodSymbol) e.sym, origin, fixFlags)) { break; } } e = e.next(); } } } } public boolean fixOverride(VisageFunctionDefinition tree, MethodSymbol m, MethodSymbol other, ClassSymbol origin, boolean fixFlags) { Type mt = types.memberType(origin.type, m); Type ot = types.memberType(origin.type, other); // Error if overriding result type is different // (or, in the case of generics mode, not a subtype) of // overridden result type. We have to rename any type parameters // before comparing types. List<Type> mtvars = mt.getTypeArguments(); List<Type> otvars = ot.getTypeArguments(); Type mtres = mt.getReturnType(); Type otres = types.subst(ot.getReturnType(), otvars, mtvars); boolean resultTypesOK = mtres != syms.visage_UnspecifiedType && types.returnTypeSubstitutable(mt, ot, otres, noteWarner); if (!resultTypesOK) { if (!source.allowCovariantReturns() && m.owner != origin && m.owner.isSubClass(other.owner, types)) { // allow limited interoperability with covariant returns } else { Type setReturnType = null; if (mtres == syms.visage_DoubleType && otres == syms.floatType) { setReturnType = syms.floatType; } else if ((mtres == syms.visage_IntegerType || mtres == syms.visage_DoubleType) && otres == syms.byteType) { setReturnType = syms.byteType; } else if ((mtres == syms.visage_IntegerType || mtres == syms.visage_DoubleType) && otres == syms.charType) { setReturnType = syms.charType; } else if ((mtres == syms.visage_IntegerType || mtres == syms.visage_DoubleType) && otres == syms.shortType) { setReturnType = syms.shortType; } else if ((mtres == syms.visage_IntegerType || mtres == syms.visage_DoubleType) && otres == syms.longType) { setReturnType = syms.longType; } else if (mtres == syms.visage_UnspecifiedType) { setReturnType = otres; } if (setReturnType != null) { VisageType oldType = tree.operation.getVisageReturnType(); tree.operation.rettype = visagemake.TypeClass(visagemake.Type(setReturnType), oldType.getCardinality()); if (mt instanceof MethodType) { ((MethodType)mt).restype = setReturnType; } if (tree.type != null && tree.type instanceof MethodType) { ((MethodType)tree.type).restype = setReturnType; } } } } // now fix up the access modifiers if (fixFlags) { long origFlags = m.flags(); long flags = origFlags; if ((flags & VisageFlags.VisageExplicitAccessFlags) == 0) { flags |= other.flags() & (VisageFlags.VisageExplicitAccessFlags | VisageFlags.VisageAccessFlags); } if (flags != origFlags) { m.flags_field = flags; tree.getModifiers().flags = flags; } } return true; } public void visitTimeLiteral(VisageTimeLiteral tree) { result = check(tree, syms.visage_DurationType, VAL, pkind, pt, pSequenceness); } public void visitLengthLiteral(VisageLengthLiteral tree) { result = check(tree, syms.visage_LengthType, VAL, pkind, pt, pSequenceness); } public void visitAngleLiteral(VisageAngleLiteral tree) { result = check(tree, syms.visage_AngleType, VAL, pkind, pt, pSequenceness); } public void visitColorLiteral(VisageColorLiteral tree) { result = check(tree, syms.visage_ColorType, VAL, pkind, pt, pSequenceness); } public void visitInterpolateValue(VisageInterpolateValue tree) { VisageEnv<VisageAttrContext> dupEnv = env.dup(tree); dupEnv.outer = env; VisageTag tag = VisageTreeInfo.skipParens(tree.attribute).getVisageTag(); Type instType; if (tag == VisageTag.IDENT || tag == VisageTag.SELECT) { instType = attribTree(tree.attribute, dupEnv, VAR, Type.noType); tree.sym = VisageTreeInfo.symbol(tree.attribute); if (instType == null || instType == syms.visage_UnspecifiedType) instType = Type.noType; if (tag == VisageTag.SELECT) { VisageSelect select = (VisageSelect) tree.attribute; if (chk.checkBidiSelect(select, env, pt)) log.warning(select.getExpression().pos(), MsgSym.MESSAGE_SELECT_TARGET_NOT_REEVALUATED_FOR_ANIM, select.getExpression(), select.name); } } else { instType = Type.noType; log.error(tree.pos(), MsgSym.MESSAGE_UNEXPECTED_TYPE, Resolve.kindNames(VAR), Resolve.kindName(VAL)); } attribExpr(tree.value, dupEnv, instType); if (tree.interpolation != null) attribExpr(tree.interpolation, dupEnv); //TODO: this is evil //wrap it in a function /* * VSGC-3133 -- previously, we filled "tree.value" with anon * FunctionValue. But, if this VisageInterpolateValue tree is * attributed twice, on the second visit, tree.value would * be a function value and so would fail with type check. * Now, we use tree.funcValue. Decomposition will copy the * "tree.funcValue" to "tree.value". */ tree.funcValue = visagemake.at(tree.pos()).FunctionValue( visagemake.at(tree.pos()).TypeUnknown(), List.<VisageVar>nil(), visagemake.at(tree.pos()).Block(0L, List.<VisageExpression>nil(), tree.value)); attribExpr(tree.funcValue, env); result = check(tree, syms.visage_KeyValueType, VAL, pkind, pt, pSequenceness); } /* private void checkInterpolationValue(VisageInterpolateValue tree, VisageExpression var) { final Type targetType; if (tree.getAttribute() != null) { VisageExpression t = tree.getAttribute(); VisageEnv<VisageAttrContext> localEnv = newLocalEnv(tree); Name attribute = names.fromString(t.toString()); Symbol memberSym = rs.findIdentInType(env, var.type, attribute, VAR); memberSym = rs.access(memberSym, t.pos(), var.type, attribute, true); memberSym.complete(); t.type = memberSym.type; t.sym = memberSym; targetType = t.type; } else targetType = var.type; Type valueType = attribExpr(tree.getValue(), env, Infer.anyPoly); Type interpolateType = syms.errType; if (types.isAssignable(valueType, syms.visage_ColorType)) { interpolateType = syms.visage_ColorInterpolatorType; } else if (types.isAssignable(valueType, syms.visage_DoubleType) || types.isAssignable(valueType, syms.visage_IntegerType)) { interpolateType = syms.visage_NumberInterpolatorType; } else { log.error(tree.pos(), "unexpected.type", Resolve.kindNames(pkind), Resolve.kindName(pkind)); interpolateType = syms.errType; } tree.type = interpolateType; result = tree.type; } */ public void visitKeyFrameLiteral(VisageKeyFrameLiteral tree) { VisageEnv<VisageAttrContext> localEnv = env.dup(tree); localEnv.outer = env; attribExpr(tree.start, localEnv); for (VisageExpression e:tree.values) { Type keyValueType = attribExpr(e, localEnv); if (keyValueType.tag != ERROR && !types.isSameType(keyValueType, syms.visage_KeyValueType)) { log.error(e, MsgSym.MESSAGE_VISAGE_KEYVALUE_REQUIRED); } } result = check(tree, syms.visage_KeyFrameType, VAL, pkind, pt, pSequenceness); } private VisageTree breakTree = null; public VisageEnv<VisageAttrContext> attribExprToTree(VisageTree expr, VisageEnv<VisageAttrContext> env, VisageTree tree) { breakTree = tree; JavaFileObject prev = log.useSource(null); try { attribExpr(expr, env); } catch (BreakAttr b) { return b.env; } finally { breakTree = null; log.useSource(prev); } return env; } public VisageEnv<VisageAttrContext> attribStatToTree(VisageTree stmt, VisageEnv<VisageAttrContext> env, VisageTree tree) { breakTree = tree; JavaFileObject prev = log.useSource(null); try { attribDecl(stmt, env); } catch (BreakAttr b) { return b.env; } finally { breakTree = null; log.useSource(prev); } return env; } private static class BreakAttr extends RuntimeException { static final long serialVersionUID = -6924771130405446405L; private VisageEnv<VisageAttrContext> env; private BreakAttr(VisageEnv<VisageAttrContext> env) { this.env = env; } } public void visitScript(VisageScript tree) { // Do not assert that we cannot reach here as this unit can // be visited by virtue of visiting VisageErronous which // will attempt to visit each Erroneous node that it has // encapsualted. // } public void visitCatch(VisageCatch tree) { // Do not assert that we cannot reach here as this unit can // be visited by virtue of visiting VisageErronous which // will attempt to visit each Erroneous node that it has // encapsualted. // } public void visitModifiers(VisageModifiers tree) { // Do not assert that we cannot reach here as this unit can // be visited by virtue of visiting VisageErronous which // will attempt to visit each Erroneous node that it has // encapsualted. // } }