/* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.comp; import java.util.*; import java.util.function.BiConsumer; import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; import javax.tools.JavaFileObject; import com.sun.source.tree.CaseTree.CaseKind; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.TreeVisitor; import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.code.TypeMetadata.Annotations; import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError; import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; import com.sun.tools.javac.comp.Check.CheckContext; import com.sun.tools.javac.comp.DeferredAttr.AttrMode; import com.sun.tools.javac.jvm.*; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArgs; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.JCDiagnostic.Warning; import com.sun.tools.javac.util.List; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.ANNOTATION; import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Kinds.*; import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.code.TypeTag.WILDCARD; import com.sun.tools.javac.comp.Analyzer.AnalyzerMode; import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; /** 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 is NOT part of any supported API. * 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. */ public class Attr extends JCTree.Visitor { protected static final Context.Key attrKey = new Context.Key<>(); final Names names; final Log log; final Symtab syms; final Resolve rs; final Operators operators; final Infer infer; final Analyzer analyzer; final DeferredAttr deferredAttr; final Check chk; final Flow flow; final MemberEnter memberEnter; final TypeEnter typeEnter; final TreeMaker make; final ConstFold cfolder; final Enter enter; final Target target; final Types types; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; final DeferredLintHandler deferredLintHandler; final TypeEnvs typeEnvs; final Dependencies dependencies; final Annotate annotate; final ArgumentAttr argumentAttr; public static Attr instance(Context context) { Attr instance = context.get(attrKey); if (instance == null) instance = new Attr(context); return instance; } protected Attr(Context context) { context.put(attrKey, this); names = Names.instance(context); log = Log.instance(context); syms = Symtab.instance(context); rs = Resolve.instance(context); operators = Operators.instance(context); chk = Check.instance(context); flow = Flow.instance(context); memberEnter = MemberEnter.instance(context); typeEnter = TypeEnter.instance(context); make = TreeMaker.instance(context); enter = Enter.instance(context); infer = Infer.instance(context); analyzer = Analyzer.instance(context); deferredAttr = DeferredAttr.instance(context); cfolder = ConstFold.instance(context); target = Target.instance(context); types = Types.instance(context); diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); Options options = Options.instance(context); Source source = Source.instance(context); allowPoly = Feature.POLY.allowedInSource(source); allowTypeAnnos = Feature.TYPE_ANNOTATIONS.allowedInSource(source); allowLambda = Feature.LAMBDA.allowedInSource(source); allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source); allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source); sourceName = source.name; useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); statInfo = new ResultInfo(KindSelector.NIL, Type.noType); varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType); unknownExprInfo = new ResultInfo(KindSelector.VAL, Type.noType); methodAttrInfo = new MethodAttrInfo(); unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType); unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType); recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext); } /** Switch: support target-typing inference */ boolean allowPoly; /** Switch: support type annotations. */ boolean allowTypeAnnos; /** Switch: support lambda expressions ? */ boolean allowLambda; /** Switch: support default methods ? */ boolean allowDefaultMethods; /** Switch: static interface methods enabled? */ boolean allowStaticInterfaceMethods; /** * Switch: warn about use of variable before declaration? * RFE: 6425594 */ boolean useBeforeDeclarationWarning; /** * Switch: name of source level; used for error reporting. */ String sourceName; /** 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. * It is not necessary in this case since we know that kind and type * are correct. * * @param tree The tree whose kind and type is checked * @param found The computed type of the tree * @param ownkind The computed kind of the tree * @param resultInfo The expected result of the tree */ Type check(final JCTree tree, final Type found, final KindSelector ownkind, final ResultInfo resultInfo) { InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext(); Type owntype; boolean shouldCheck = !found.hasTag(ERROR) && !resultInfo.pt.hasTag(METHOD) && !resultInfo.pt.hasTag(FORALL); if (shouldCheck && !ownkind.subset(resultInfo.pkind)) { log.error(tree.pos(), Errors.UnexpectedType(resultInfo.pkind.kindNames(), ownkind.kindNames())); owntype = types.createErrorType(found); } else if (allowPoly && inferenceContext.free(found)) { //delay the check if there are inference variables in the found type //this means we are dealing with a partially inferred poly expression owntype = shouldCheck ? resultInfo.pt : found; if (resultInfo.checkMode.installPostInferenceHook()) { inferenceContext.addFreeTypeListener(List.of(found), instantiatedContext -> { ResultInfo pendingResult = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt)); check(tree, inferenceContext.asInstType(found), ownkind, pendingResult); }); } } else { owntype = shouldCheck ? resultInfo.check(tree, found) : found; } if (resultInfo.checkMode.updateTreeType()) { tree.type = owntype; } return owntype; } /** Is given blank final variable assignable, i.e. in a scope where it * may be assigned to even though it is final? * @param v The blank final variable. * @param env The current environment. */ boolean isAssignableAsBlankFinal(VarSymbol v, Env env) { Symbol owner = env.info.scope.owner; // owner refers to the innermost variable, method or // initializer block declaration at this point. return v.owner == owner || ((owner.name == names.init || // i.e. we are in a constructor owner.kind == VAR || // i.e. we are in a variable initializer (owner.flags() & BLOCK) != 0) // i.e. we are in an initializer block && v.owner == owner.owner && ((v.flags() & STATIC) != 0) == Resolve.isStatic(env)); } /** Check that variable can be assigned to. * @param pos The current source code position. * @param v The assigned variable * @param base If the variable is referred to in a Select, the part * to the left of the `.', null otherwise. * @param env The current environment. */ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env env) { if (v.name == names._this) { log.error(pos, Errors.CantAssignValToThis); } else if ((v.flags() & FINAL) != 0 && ((v.flags() & HASINIT) != 0 || !((base == null || TreeInfo.isThisQualifier(base)) && isAssignableAsBlankFinal(v, env)))) { if (v.isResourceVariable()) { //TWR resource log.error(pos, Errors.TryResourceMayNotBeAssigned(v)); } else { log.error(pos, Errors.CantAssignValToFinalVar(v)); } } } /** 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(JCTree tree) { if (tree.hasTag(SELECT)) { Symbol lsym = TreeInfo.symbol(((JCFieldAccess) tree).selected); if (lsym == null || lsym.kind != TYP) { return false; } } return true; } /** 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, Env env) { return rs.resolveSelf(pos, env, env.enclClass.sym, names._this); } /** Attribute a parsed identifier. * @param tree Parsed identifier name * @param topLevel The toplevel to use */ public Symbol attribIdent(JCTree tree, JCCompilationUnit topLevel) { Env localEnv = enter.topLevelEnv(topLevel); localEnv.enclClass = make.ClassDef(make.Modifiers(0), syms.errSymbol.name, null, null, null, null); localEnv.enclClass.sym = syms.errSymbol; return attribIdent(tree, localEnv); } /** Attribute a parsed identifier. * @param tree Parsed identifier name * @param env The env to use */ public Symbol attribIdent(JCTree tree, Env env) { return tree.accept(identAttributer, env); } // where private TreeVisitor> identAttributer = new IdentAttributer(); private class IdentAttributer extends SimpleTreeVisitor> { @Override @DefinedBy(Api.COMPILER_TREE) public Symbol visitMemberSelect(MemberSelectTree node, Env env) { Symbol site = visit(node.getExpression(), env); if (site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN) return site; Name name = (Name)node.getIdentifier(); if (site.kind == PCK) { env.toplevel.packge = (PackageSymbol)site; return rs.findIdentInPackage(env, (TypeSymbol)site, name, KindSelector.TYP_PCK); } else { env.enclClass.sym = (ClassSymbol)site; return rs.findMemberType(env, site.asType(), name, (TypeSymbol)site); } } @Override @DefinedBy(Api.COMPILER_TREE) public Symbol visitIdentifier(IdentifierTree node, Env env) { return rs.findIdent(env, (Name)node.getName(), KindSelector.TYP_PCK); } } public Type coerce(Type etype, Type ttype) { return cfolder.coerce(etype, ttype); } public Type attribType(JCTree node, TypeSymbol sym) { Env env = typeEnvs.get(sym); Env localEnv = env.dup(node, env.info.dup()); return attribTree(node, localEnv, unknownTypeInfo); } public Type attribImportQualifier(JCImport tree, Env env) { // Attribute qualifying package or class. JCFieldAccess s = (JCFieldAccess)tree.qualid; return attribTree(s.selected, env, new ResultInfo(tree.staticImport ? KindSelector.TYP : KindSelector.TYP_PCK, Type.noType)); } public Env attribExprToTree(JCTree expr, Env env, JCTree tree) { breakTree = tree; JavaFileObject prev = log.useSource(env.toplevel.sourcefile); EnumSet analyzerModes = EnumSet.copyOf(analyzer.analyzerModes); try { analyzer.analyzerModes.clear(); attribExpr(expr, env); } catch (BreakAttr b) { return b.env; } catch (AssertionError ae) { if (ae.getCause() instanceof BreakAttr) { return ((BreakAttr)(ae.getCause())).env; } else { throw ae; } } finally { breakTree = null; log.useSource(prev); analyzer.analyzerModes.addAll(analyzerModes); } return env; } public Env attribStatToTree(JCTree stmt, Env env, JCTree tree) { breakTree = tree; JavaFileObject prev = log.useSource(env.toplevel.sourcefile); EnumSet analyzerModes = EnumSet.copyOf(analyzer.analyzerModes); try { analyzer.analyzerModes.clear(); attribStat(stmt, env); } catch (BreakAttr b) { return b.env; } catch (AssertionError ae) { if (ae.getCause() instanceof BreakAttr) { return ((BreakAttr)(ae.getCause())).env; } else { throw ae; } } finally { breakTree = null; log.useSource(prev); analyzer.analyzerModes.addAll(analyzerModes); } return env; } private JCTree breakTree = null; private static class BreakAttr extends RuntimeException { static final long serialVersionUID = -6924771130405446405L; private Env env; private BreakAttr(Env env) { this.env = env; } } /** * Mode controlling behavior of Attr.Check */ enum CheckMode { NORMAL, /** * Mode signalling 'fake check' - skip tree update. A side-effect of this mode is * that the captured var cache in {@code InferenceContext} will be used in read-only * mode when performing inference checks. */ NO_TREE_UPDATE { @Override public boolean updateTreeType() { return false; } }, /** * Mode signalling that caller will manage free types in tree decorations. */ NO_INFERENCE_HOOK { @Override public boolean installPostInferenceHook() { return false; } }; public boolean updateTreeType() { return true; } public boolean installPostInferenceHook() { return true; } } class ResultInfo { final KindSelector pkind; final Type pt; final CheckContext checkContext; final CheckMode checkMode; ResultInfo(KindSelector pkind, Type pt) { this(pkind, pt, chk.basicHandler, CheckMode.NORMAL); } ResultInfo(KindSelector pkind, Type pt, CheckMode checkMode) { this(pkind, pt, chk.basicHandler, checkMode); } protected ResultInfo(KindSelector pkind, Type pt, CheckContext checkContext) { this(pkind, pt, checkContext, CheckMode.NORMAL); } protected ResultInfo(KindSelector pkind, Type pt, CheckContext checkContext, CheckMode checkMode) { this.pkind = pkind; this.pt = pt; this.checkContext = checkContext; this.checkMode = checkMode; } /** * Should {@link Attr#attribTree} use the {@ArgumentAttr} visitor instead of this one? * @param tree The tree to be type-checked. * @return true if {@ArgumentAttr} should be used. */ protected boolean needsArgumentAttr(JCTree tree) { return false; } protected Type check(final DiagnosticPosition pos, final Type found) { return chk.checkType(pos, found, pt, checkContext); } protected ResultInfo dup(Type newPt) { return new ResultInfo(pkind, newPt, checkContext, checkMode); } protected ResultInfo dup(CheckContext newContext) { return new ResultInfo(pkind, pt, newContext, checkMode); } protected ResultInfo dup(Type newPt, CheckContext newContext) { return new ResultInfo(pkind, newPt, newContext, checkMode); } protected ResultInfo dup(Type newPt, CheckContext newContext, CheckMode newMode) { return new ResultInfo(pkind, newPt, newContext, newMode); } protected ResultInfo dup(CheckMode newMode) { return new ResultInfo(pkind, pt, checkContext, newMode); } @Override public String toString() { if (pt != null) { return pt.toString(); } else { return ""; } } } class MethodAttrInfo extends ResultInfo { public MethodAttrInfo() { this(chk.basicHandler); } public MethodAttrInfo(CheckContext checkContext) { super(KindSelector.VAL, Infer.anyPoly, checkContext); } @Override protected boolean needsArgumentAttr(JCTree tree) { return true; } protected ResultInfo dup(Type newPt) { throw new IllegalStateException(); } protected ResultInfo dup(CheckContext newContext) { return new MethodAttrInfo(newContext); } protected ResultInfo dup(Type newPt, CheckContext newContext) { throw new IllegalStateException(); } protected ResultInfo dup(Type newPt, CheckContext newContext, CheckMode newMode) { throw new IllegalStateException(); } protected ResultInfo dup(CheckMode newMode) { throw new IllegalStateException(); } } class RecoveryInfo extends ResultInfo { public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) { this(deferredAttrContext, Type.recoveryType); } public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext, Type pt) { super(KindSelector.VAL, pt, new Check.NestedCheckContext(chk.basicHandler) { @Override public DeferredAttr.DeferredAttrContext deferredAttrContext() { return deferredAttrContext; } @Override public boolean compatible(Type found, Type req, Warner warn) { return true; } @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { if (pt == Type.recoveryType) { chk.basicHandler.report(pos, details); } } }); } } final ResultInfo statInfo; final ResultInfo varAssignmentInfo; final ResultInfo methodAttrInfo; final ResultInfo unknownExprInfo; final ResultInfo unknownTypeInfo; final ResultInfo unknownTypeExprInfo; final ResultInfo recoveryInfo; Type pt() { return resultInfo.pt; } KindSelector pkind() { return resultInfo.pkind; } /* ************************************************************************ * Visitor methods *************************************************************************/ /** Visitor argument: the current environment. */ Env env; /** Visitor argument: the currently expected attribution result. */ ResultInfo resultInfo; /** Visitor result: the computed type. */ 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 resultInfo The result info visitor argument. */ Type attribTree(JCTree tree, Env env, ResultInfo resultInfo) { Env prevEnv = this.env; ResultInfo prevResult = this.resultInfo; try { this.env = env; this.resultInfo = resultInfo; if (resultInfo.needsArgumentAttr(tree)) { result = argumentAttr.attribArg(tree, env); } else { tree.accept(this); } if (tree == breakTree && resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) { breakTreeFound(copyEnv(env)); } return result; } catch (CompletionFailure ex) { tree.type = syms.errType; return chk.completionError(tree.pos(), ex); } finally { this.env = prevEnv; this.resultInfo = prevResult; } } protected void breakTreeFound(Env env) { throw new BreakAttr(env); } Env copyEnv(Env env) { Env newEnv = env.dup(env.tree, env.info.dup(copyScope(env.info.scope))); if (newEnv.outer != null) { newEnv.outer = copyEnv(newEnv.outer); } return newEnv; } WriteableScope copyScope(WriteableScope sc) { WriteableScope newScope = WriteableScope.create(sc.owner); List elemsList = List.nil(); for (Symbol sym : sc.getSymbols()) { elemsList = elemsList.prepend(sym); } for (Symbol s : elemsList) { newScope.enter(s); } return newScope; } /** Derived visitor method: attribute an expression tree. */ public Type attribExpr(JCTree tree, Env env, Type pt) { return attribTree(tree, env, new ResultInfo(KindSelector.VAL, !pt.hasTag(ERROR) ? pt : Type.noType)); } /** Derived visitor method: attribute an expression tree with * no constraints on the computed type. */ public Type attribExpr(JCTree tree, Env env) { return attribTree(tree, env, unknownExprInfo); } /** Derived visitor method: attribute a type tree. */ public Type attribType(JCTree tree, Env env) { Type result = attribType(tree, env, Type.noType); return result; } /** Derived visitor method: attribute a type tree. */ Type attribType(JCTree tree, Env env, Type pt) { Type result = attribTree(tree, env, new ResultInfo(KindSelector.TYP, pt)); return result; } /** Derived visitor method: attribute a statement or definition tree. */ public Type attribStat(JCTree tree, Env env) { Env analyzeEnv = analyzer.copyEnvIfNeeded(tree, env); try { return attribTree(tree, env, statInfo); } finally { analyzer.analyzeIfNeeded(tree, analyzeEnv); } } /** Attribute a list of expressions, returning a list of types. */ List attribExprs(List trees, Env env, Type pt) { ListBuffer ts = new ListBuffer<>(); for (List l = trees; l.nonEmpty(); l = l.tail) ts.append(attribExpr(l.head, env, pt)); return ts.toList(); } /** Attribute a list of statements, returning nothing. */ void attribStats(List trees, Env env) { for (List l = trees; l.nonEmpty(); l = l.tail) attribStat(l.head, env); } /** Attribute the arguments in a method call, returning the method kind. */ KindSelector attribArgs(KindSelector initialKind, List trees, Env env, ListBuffer argtypes) { KindSelector kind = initialKind; for (JCExpression arg : trees) { Type argtype = chk.checkNonVoid(arg, attribTree(arg, env, allowPoly ? methodAttrInfo : unknownExprInfo)); if (argtype.hasTag(DEFERRED)) { kind = KindSelector.of(KindSelector.POLY, kind); } argtypes.append(argtype); } return kind; } /** Attribute a type argument list, returning a list of types. * Caller is responsible for calling checkRefTypes. */ List attribAnyTypes(List trees, Env env) { ListBuffer argtypes = new ListBuffer<>(); for (List l = trees; l.nonEmpty(); l = l.tail) argtypes.append(attribType(l.head, env)); return argtypes.toList(); } /** Attribute a type argument list, returning a list of types. * Check that all the types are references. */ List attribTypes(List trees, Env env) { List types = attribAnyTypes(trees, env); return chk.checkRefTypes(trees, types); } /** * Attribute type variables (of generic classes or methods). * Compound types are attributed later in attribBounds. * @param typarams the type variables to enter * @param env the current environment */ void attribTypeVariables(List typarams, Env env) { for (JCTypeParameter tvar : typarams) { TypeVar a = (TypeVar)tvar.type; a.tsym.flags_field |= UNATTRIBUTED; a.bound = Type.noType; if (!tvar.bounds.isEmpty()) { List bounds = List.of(attribType(tvar.bounds.head, env)); for (JCExpression bound : tvar.bounds.tail) bounds = bounds.prepend(attribType(bound, env)); types.setBounds(a, bounds.reverse()); } else { // if no bounds are given, assume a single bound of // java.lang.Object. types.setBounds(a, List.of(syms.objectType)); } a.tsym.flags_field &= ~UNATTRIBUTED; } for (JCTypeParameter tvar : typarams) { chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type); } } /** * Attribute the type references in a list of annotations. */ void attribAnnotationTypes(List annotations, Env env) { for (List al = annotations; al.nonEmpty(); al = al.tail) { JCAnnotation a = al.head; attribType(a.annotationType, env); } } /** * Attribute a "lazy constant value". * @param env The env for the const value * @param variable The initializer for the const value * @param type The expected type, or null * @see VarSymbol#setLazyConstValue */ public Object attribLazyConstantValue(Env env, JCVariableDecl variable, Type type) { DiagnosticPosition prevLintPos = deferredLintHandler.setPos(variable.pos()); final JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { Type itype = attribExpr(variable.init, env, type); if (variable.isImplicitlyTyped()) { //fixup local variable type type = variable.type = variable.sym.type = chk.checkLocalVarType(variable, itype.baseType(), variable.name); } if (itype.constValue() != null) { return coerce(itype, type).constValue(); } else { return null; } } finally { log.useSource(prevSource); deferredLintHandler.setPos(prevLintPos); } } /** Attribute type reference in an `extends' or `implements' clause. * Supertypes of anonymous inner classes are usually already attributed. * * @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(JCTree tree, Env env, boolean classExpected, boolean interfaceExpected, boolean checkExtensible) { Type t = tree.type != null ? tree.type : attribType(tree, env); return checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible); } Type checkBase(Type t, JCTree tree, Env env, boolean classExpected, boolean interfaceExpected, boolean checkExtensible) { final DiagnosticPosition pos = tree.hasTag(TYPEAPPLY) ? (((JCTypeApply) tree).clazz).pos() : tree.pos(); if (t.tsym.isAnonymous()) { log.error(pos, Errors.CantInheritFromAnon); return types.createErrorType(t); } if (t.isErroneous()) return t; if (t.hasTag(TYPEVAR) && !classExpected && !interfaceExpected) { // check that type variable is already visible if (t.getUpperBound() == null) { log.error(pos, Errors.IllegalForwardRef); return types.createErrorType(t); } } else { t = chk.checkClassType(pos, t, checkExtensible); } if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) { log.error(pos, Errors.IntfExpectedHere); // return errType is necessary since otherwise there might // be undetected cycles which cause attribution to loop return types.createErrorType(t); } else if (checkExtensible && classExpected && (t.tsym.flags() & INTERFACE) != 0) { log.error(pos, Errors.NoIntfExpectedHere); return types.createErrorType(t); } if (checkExtensible && ((t.tsym.flags() & FINAL) != 0)) { log.error(pos, Errors.CantInheritFromFinal(t.tsym)); } chk.checkNonCyclic(pos, t); return t; } Type attribIdentAsEnumType(Env env, JCIdent id) { Assert.check((env.enclClass.sym.flags() & ENUM) != 0); id.type = env.info.scope.owner.enclClass().type; id.sym = env.info.scope.owner.enclClass(); return id.type; } public void visitClassDef(JCClassDecl tree) { Optional localCacheContext = Optional.ofNullable(env.info.isSpeculative ? argumentAttr.withLocalCacheContext() : null); try { // Local and anonymous classes have not been entered yet, so we need to // do it now. if (env.info.scope.owner.kind.matches(KindSelector.VAL_MTH)) { enter.classEnter(tree, env); } else { // If this class declaration is part of a class level annotation, // as in @MyAnno(new Object() {}) class MyClass {}, enter it in // order to simplify later steps and allow for sensible error // messages. if (env.tree.hasTag(NEWCLASS) && TreeInfo.isInAnnotation(env, tree)) 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(); // If this class appears as an anonymous class // in a superclass constructor call // disable implicit outer instance from being passed. // (This would be an illegal access to "this before super"). if (env.info.isSelfCall && env.tree.hasTag(NEWCLASS)) { c.flags_field |= NOOUTERTHIS; } attribClass(tree.pos(), c); result = tree.type = c.type; } } finally { localCacheContext.ifPresent(LocalCacheContext::leave); } } public void visitMethodDef(JCMethodDecl tree) { MethodSymbol m = tree.sym; boolean isDefaultMethod = (m.flags() & DEFAULT) != 0; Lint lint = env.info.lint.augment(m); Lint prevLint = chk.setLint(lint); MethodSymbol prevMethod = chk.setMethod(m); try { deferredLintHandler.flush(tree.pos()); chk.checkDeprecatedAnnotation(tree.pos(), m); // Create a new environment with local scope // for attributing the method. Env localEnv = memberEnter.methodEnv(tree, env); localEnv.info.lint = lint; attribStats(tree.typarams, localEnv); // If we override any other methods, check that we do so properly. // JLS ??? if (m.isStatic()) { chk.checkHideClashes(tree.pos(), env.enclClass.type, m); } else { chk.checkOverrideClashes(tree.pos(), env.enclClass.type, m); } chk.checkOverride(env, tree, m); if (isDefaultMethod && types.overridesObjectMethod(m.enclClass(), m)) { log.error(tree, Errors.DefaultOverridesObjectMember(m.name, Kinds.kindName(m.location()), m.location())); } // Enter all type parameters into the local method scope. for (List l = tree.typarams; l.nonEmpty(); l = l.tail) localEnv.info.scope.enterIfAbsent(l.head.type.tsym); ClassSymbol owner = env.enclClass.sym; if ((owner.flags() & ANNOTATION) != 0 && (tree.params.nonEmpty() || tree.recvparam != null)) log.error(tree.params.nonEmpty() ? tree.params.head.pos() : tree.recvparam.pos(), Errors.IntfAnnotationMembersCantHaveParams); // Attribute all value parameters. for (List l = tree.params; l.nonEmpty(); l = l.tail) { attribStat(l.head, localEnv); } chk.checkVarargsMethodDecl(localEnv, tree); // Check that type parameters are well-formed. chk.validate(tree.typarams, localEnv); // Check that result type is well-formed. if (tree.restype != null && !tree.restype.type.hasTag(VOID)) chk.validate(tree.restype, localEnv); // Check that receiver type is well-formed. if (tree.recvparam != null) { // Use a new environment to check the receiver parameter. // Otherwise I get "might not have been initialized" errors. // Is there a better way? Env newEnv = memberEnter.methodEnv(tree, env); attribType(tree.recvparam, newEnv); chk.validate(tree.recvparam, newEnv); } // annotation method checks if ((owner.flags() & ANNOTATION) != 0) { // annotation method cannot have throws clause if (tree.thrown.nonEmpty()) { log.error(tree.thrown.head.pos(), Errors.ThrowsNotAllowedInIntfAnnotation); } // annotation method cannot declare type-parameters if (tree.typarams.nonEmpty()) { log.error(tree.typarams.head.pos(), Errors.IntfAnnotationMembersCantHaveTypeParams); } // validate annotation method's return type (could be an annotation type) chk.validateAnnotationType(tree.restype); // ensure that annotation method does not clash with members of Object/Annotation chk.validateAnnotationMethod(tree.pos(), m); } for (List l = tree.thrown; l.nonEmpty(); l = l.tail) chk.checkType(l.head.pos(), l.head.type, syms.throwableType); if (tree.body == null) { // Empty bodies are only allowed for // abstract, native, or interface methods, or for methods // in a retrofit signature class. if (tree.defaultValue != null) { if ((owner.flags() & ANNOTATION) == 0) log.error(tree.pos(), Errors.DefaultAllowedInIntfAnnotationMember); } if (isDefaultMethod || (tree.sym.flags() & (ABSTRACT | NATIVE)) == 0) log.error(tree.pos(), Errors.MissingMethBodyOrDeclAbstract); } else if ((tree.sym.flags() & (ABSTRACT|DEFAULT|PRIVATE)) == ABSTRACT) { if ((owner.flags() & INTERFACE) != 0) { log.error(tree.body.pos(), Errors.IntfMethCantHaveBody); } else { log.error(tree.pos(), Errors.AbstractMethCantHaveBody); } } else if ((tree.mods.flags & NATIVE) != 0) { log.error(tree.pos(), Errors.NativeMethCantHaveBody); } else { // Add an implicit super() call unless an explicit call to // super(...) or this(...) is given // or we are compiling class java.lang.Object. if (tree.name == names.init && owner.type != syms.objectType) { JCBlock body = tree.body; if (body.stats.isEmpty() || !TreeInfo.isSelfCall(body.stats.head)) { body.stats = body.stats. prepend(typeEnter.SuperCall(make.at(body.pos), List.nil(), List.nil(), false)); } else if ((env.enclClass.sym.flags() & ENUM) != 0 && (tree.mods.flags & GENERATEDCONSTR) == 0 && TreeInfo.isSuperCall(body.stats.head)) { // enum constructors are not allowed to call super // directly, so make sure there aren't any super calls // in enum constructors, except in the compiler // generated one. log.error(tree.body.stats.head.pos(), Errors.CallToSuperNotAllowedInEnumCtor(env.enclClass.sym)); } } // Attribute all type annotations in the body annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null); annotate.flush(); // Attribute method body. attribStat(tree.body, localEnv); } localEnv.info.scope.leave(); result = tree.type = m.type; } finally { chk.setLint(prevLint); chk.setMethod(prevMethod); } } public void visitVarDef(JCVariableDecl tree) { // Local variables have not been entered yet, so we need to do it now: if (env.info.scope.owner.kind == MTH) { if (tree.sym != null) { // parameters have already been entered env.info.scope.enter(tree.sym); } else { if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0) { if (tree.init == null) { //cannot use 'var' without initializer log.error(tree, Errors.CantInferLocalVarType(tree.name, Fragments.LocalMissingInit)); tree.vartype = make.Erroneous(); } else { Fragment msg = canInferLocalVarType(tree); if (msg != null) { //cannot use 'var' with initializer which require an explicit target //(e.g. lambda, method reference, array initializer). log.error(tree, Errors.CantInferLocalVarType(tree.name, msg)); tree.vartype = make.Erroneous(); } } } try { annotate.blockAnnotations(); memberEnter.memberEnter(tree, env); } finally { annotate.unblockAnnotations(); } } } else { if (tree.init != null) { // Field initializer expression need to be entered. annotate.queueScanTreeAndTypeAnnotate(tree.init, env, tree.sym, tree.pos()); annotate.flush(); } } VarSymbol v = tree.sym; Lint lint = env.info.lint.augment(v); Lint prevLint = chk.setLint(lint); // Check that the variable's declared type is well-formed. boolean isImplicitLambdaParameter = env.tree.hasTag(LAMBDA) && ((JCLambda)env.tree).paramKind == JCLambda.ParameterKind.IMPLICIT && (tree.sym.flags() & PARAMETER) != 0; chk.validate(tree.vartype, env, !isImplicitLambdaParameter && !tree.isImplicitlyTyped()); try { v.getConstValue(); // ensure compile-time constant initializer is evaluated deferredLintHandler.flush(tree.pos()); chk.checkDeprecatedAnnotation(tree.pos(), v); if (tree.init != null) { if ((v.flags_field & FINAL) == 0 || !memberEnter.needsLazyConstValue(tree.init)) { // Not a compile-time constant // Attribute initializer in a new environment // with the declared variable as owner. // Check that initializer conforms to variable's declared type. Env initEnv = memberEnter.initEnv(tree, env); initEnv.info.lint = lint; // 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; attribExpr(tree.init, initEnv, v.type); if (tree.isImplicitlyTyped()) { //fixup local variable type v.type = chk.checkLocalVarType(tree, tree.init.type.baseType(), tree.name); } } if (tree.isImplicitlyTyped()) { setSyntheticVariableType(tree, v.type); } } result = tree.type = v.type; } finally { chk.setLint(prevLint); } } Fragment canInferLocalVarType(JCVariableDecl tree) { LocalInitScanner lis = new LocalInitScanner(); lis.scan(tree.init); return lis.badInferenceMsg; } static class LocalInitScanner extends TreeScanner { Fragment badInferenceMsg = null; boolean needsTarget = true; @Override public void visitNewArray(JCNewArray tree) { if (tree.elemtype == null && needsTarget) { badInferenceMsg = Fragments.LocalArrayMissingTarget; } } @Override public void visitLambda(JCLambda tree) { if (needsTarget) { badInferenceMsg = Fragments.LocalLambdaMissingTarget; } } @Override public void visitTypeCast(JCTypeCast tree) { boolean prevNeedsTarget = needsTarget; try { needsTarget = false; super.visitTypeCast(tree); } finally { needsTarget = prevNeedsTarget; } } @Override public void visitReference(JCMemberReference tree) { if (needsTarget) { badInferenceMsg = Fragments.LocalMrefMissingTarget; } } @Override public void visitNewClass(JCNewClass tree) { boolean prevNeedsTarget = needsTarget; try { needsTarget = false; super.visitNewClass(tree); } finally { needsTarget = prevNeedsTarget; } } @Override public void visitApply(JCMethodInvocation tree) { boolean prevNeedsTarget = needsTarget; try { needsTarget = false; super.visitApply(tree); } finally { needsTarget = prevNeedsTarget; } } } public void visitSkip(JCSkip tree) { result = null; } public void visitBlock(JCBlock tree) { 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. Symbol fakeOwner = new MethodSymbol(tree.flags | BLOCK | env.info.scope.owner.flags() & STRICTFP, names.empty, null, env.info.scope.owner); final Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner))); if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++; // Attribute all type annotations in the block annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null); annotate.flush(); attribStats(tree.stats, localEnv); { // Store init and clinit type annotations with the ClassSymbol // to allow output in Gen.normalizeDefs. ClassSymbol cs = (ClassSymbol)env.info.scope.owner; List tas = localEnv.info.scope.owner.getRawTypeAttributes(); if ((tree.flags & STATIC) != 0) { cs.appendClassInitTypeAttributes(tas); } else { cs.appendInitTypeAttributes(tas); } } } else { // Create a new local environment with a local scope. Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); try { attribStats(tree.stats, localEnv); } finally { localEnv.info.scope.leave(); } } result = null; } public void visitDoLoop(JCDoWhileLoop tree) { attribStat(tree.body, env.dup(tree)); attribExpr(tree.cond, env, syms.booleanType); result = null; } public void visitWhileLoop(JCWhileLoop tree) { attribExpr(tree.cond, env, syms.booleanType); attribStat(tree.body, env.dup(tree)); result = null; } public void visitForLoop(JCForLoop tree) { Env loopEnv = env.dup(env.tree, env.info.dup(env.info.scope.dup())); try { attribStats(tree.init, loopEnv); if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType); loopEnv.tree = tree; // before, we were not in loop! attribStats(tree.step, loopEnv); attribStat(tree.body, loopEnv); result = null; } finally { loopEnv.info.scope.leave(); } } public void visitForeachLoop(JCEnhancedForLoop tree) { Env loopEnv = env.dup(env.tree, env.info.dup(env.info.scope.dup())); try { //the Formal Parameter of a for-each loop is not in the scope when //attributing the for-each expression; we mimick this by attributing //the for-each expression first (against original scope). Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv)); chk.checkNonVoid(tree.pos(), exprType); Type elemtype = types.elemtype(exprType); // perhaps expr is an array? if (elemtype == null) { // or perhaps expr implements Iterable? Type base = types.asSuper(exprType, syms.iterableType.tsym); if (base == null) { log.error(tree.expr.pos(), Errors.ForeachNotApplicableToType(exprType, Fragments.TypeReqArrayOrIterable)); elemtype = types.createErrorType(exprType); } else { List iterableParams = base.allparams(); elemtype = iterableParams.isEmpty() ? syms.objectType : types.wildUpperBound(iterableParams.head); } } if (tree.var.isImplicitlyTyped()) { Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name); setSyntheticVariableType(tree.var, inferredType); } attribStat(tree.var, loopEnv); chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type); loopEnv.tree = tree; // before, we were not in loop! attribStat(tree.body, loopEnv); result = null; } finally { loopEnv.info.scope.leave(); } } public void visitLabelled(JCLabeledStatement tree) { // Check that label is not used in an enclosing statement Env env1 = env; while (env1 != null && !env1.tree.hasTag(CLASSDEF)) { if (env1.tree.hasTag(LABELLED) && ((JCLabeledStatement) env1.tree).label == tree.label) { log.error(tree.pos(), Errors.LabelAlreadyInUse(tree.label)); break; } env1 = env1.next; } attribStat(tree.body, env.dup(tree)); result = null; } public void visitSwitch(JCSwitch tree) { handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> { attribStats(c.stats, caseEnv); }); result = null; } public void visitSwitchExpression(JCSwitchExpression tree) { tree.polyKind = (pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly) ? PolyKind.STANDALONE : PolyKind.POLY; if (tree.polyKind == PolyKind.POLY && resultInfo.pt.hasTag(VOID)) { //this means we are returning a poly conditional from void-compatible lambda expression resultInfo.checkContext.report(tree, diags.fragment(Fragments.SwitchExpressionTargetCantBeVoid)); result = tree.type = types.createErrorType(resultInfo.pt); return; } ResultInfo condInfo = tree.polyKind == PolyKind.STANDALONE ? unknownExprInfo : resultInfo.dup(switchExpressionContext(resultInfo.checkContext)); ListBuffer caseTypePositions = new ListBuffer<>(); ListBuffer caseTypes = new ListBuffer<>(); handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> { caseEnv.info.breakResult = condInfo; attribStats(c.stats, caseEnv); new TreeScanner() { @Override public void visitBreak(JCBreak brk) { if (brk.target == tree) { caseTypePositions.append(brk.value != null ? brk.value.pos() : brk.pos()); caseTypes.append(brk.value != null ? brk.value.type : syms.errType); } super.visitBreak(brk); } @Override public void visitClassDef(JCClassDecl tree) {} @Override public void visitLambda(JCLambda tree) {} }.scan(c.stats); }); if (tree.cases.isEmpty()) { log.error(tree.pos(), Errors.SwitchExpressionEmpty); } Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(caseTypePositions.toList(), caseTypes.toList()) : pt(); result = tree.type = check(tree, owntype, KindSelector.VAL, resultInfo); } //where: CheckContext switchExpressionContext(CheckContext checkContext) { return new Check.NestedCheckContext(checkContext) { //this will use enclosing check context to check compatibility of //subexpression against target type; if we are in a method check context, //depending on whether boxing is allowed, we could have incompatibilities @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { enclosingContext.report(pos, diags.fragment(Fragments.IncompatibleTypeInSwitchExpression(details))); } }; } private void handleSwitch(JCTree switchTree, JCExpression selector, List cases, BiConsumer> attribCase) { Type seltype = attribExpr(selector, env); Env switchEnv = env.dup(switchTree, env.info.dup(env.info.scope.dup())); try { boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0; boolean stringSwitch = types.isSameType(seltype, syms.stringType); if (!enumSwitch && !stringSwitch) seltype = chk.checkType(selector.pos(), seltype, syms.intType); // Attribute all cases and // check that there are no duplicate case labels or default clauses. Set labels = new HashSet<>(); // The set of case labels. boolean hasDefault = false; // Is there a default label? @SuppressWarnings("removal") CaseKind caseKind = null; boolean wasError = false; for (List l = cases; l.nonEmpty(); l = l.tail) { JCCase c = l.head; if (caseKind == null) { caseKind = c.caseKind; } else if (caseKind != c.caseKind && !wasError) { log.error(c.pos(), Errors.SwitchMixingCaseTypes); wasError = true; } if (c.getExpressions().nonEmpty()) { for (JCExpression pat : c.getExpressions()) { if (TreeInfo.isNull(pat)) { log.error(pat.pos(), Errors.SwitchNullNotAllowed); } else if (enumSwitch) { Symbol sym = enumConstant(pat, seltype); if (sym == null) { log.error(pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); } else if (!labels.add(sym)) { log.error(c.pos(), Errors.DuplicateCaseLabel); } } else { Type pattype = attribExpr(pat, switchEnv, seltype); if (!pattype.hasTag(ERROR)) { if (pattype.constValue() == null) { log.error(pat.pos(), (stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq)); } else if (!labels.add(pattype.constValue())) { log.error(c.pos(), Errors.DuplicateCaseLabel); } } } } } else if (hasDefault) { log.error(c.pos(), Errors.DuplicateDefaultLabel); } else { hasDefault = true; } Env caseEnv = switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup())); try { attribCase.accept(c, caseEnv); } finally { caseEnv.info.scope.leave(); addVars(c.stats, switchEnv.info.scope); } } } finally { switchEnv.info.scope.leave(); } } // where /** Add any variables defined in stats to the switch scope. */ private static void addVars(List stats, WriteableScope switchScope) { for (;stats.nonEmpty(); stats = stats.tail) { JCTree stat = stats.head; if (stat.hasTag(VARDEF)) switchScope.enter(((JCVariableDecl) stat).sym); } } // where /** Return the selected enumeration constant symbol, or null. */ private Symbol enumConstant(JCTree tree, Type enumType) { if (tree.hasTag(IDENT)) { JCIdent ident = (JCIdent)tree; Name name = ident.name; for (Symbol sym : enumType.tsym.members().getSymbolsByName(name)) { if (sym.kind == VAR) { Symbol s = ident.sym = sym; ((VarSymbol)s).getConstValue(); // ensure initializer is evaluated ident.type = s.type; return ((s.flags_field & Flags.ENUM) == 0) ? null : s; } } } return null; } public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); attribStat(tree.body, env); result = null; } public void visitTry(JCTry tree) { // Create a new local environment with a local Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); try { boolean isTryWithResource = tree.resources.nonEmpty(); // Create a nested environment for attributing the try block if needed Env tryEnv = isTryWithResource ? env.dup(tree, localEnv.info.dup(localEnv.info.scope.dup())) : localEnv; try { // Attribute resource declarations for (JCTree resource : tree.resources) { CheckContext twrContext = new Check.NestedCheckContext(resultInfo.checkContext) { @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { chk.basicHandler.report(pos, diags.fragment(Fragments.TryNotApplicableToType(details))); } }; ResultInfo twrResult = new ResultInfo(KindSelector.VAR, syms.autoCloseableType, twrContext); if (resource.hasTag(VARDEF)) { attribStat(resource, tryEnv); twrResult.check(resource, resource.type); //check that resource type cannot throw InterruptedException checkAutoCloseable(resource.pos(), localEnv, resource.type); VarSymbol var = ((JCVariableDecl) resource).sym; var.setData(ElementKind.RESOURCE_VARIABLE); } else { attribTree(resource, tryEnv, twrResult); } } // Attribute body attribStat(tree.body, tryEnv); } finally { if (isTryWithResource) tryEnv.info.scope.leave(); } // Attribute catch clauses for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { JCCatch c = l.head; Env catchEnv = localEnv.dup(c, localEnv.info.dup(localEnv.info.scope.dup())); try { Type ctype = attribStat(c.param, catchEnv); if (TreeInfo.isMultiCatch(c)) { //multi-catch parameter is implicitly marked as final c.param.sym.flags_field |= FINAL | UNION; } if (c.param.sym.kind == VAR) { c.param.sym.setData(ElementKind.EXCEPTION_PARAMETER); } chk.checkType(c.param.vartype.pos(), chk.checkClassType(c.param.vartype.pos(), ctype), syms.throwableType); attribStat(c.body, catchEnv); } finally { catchEnv.info.scope.leave(); } } // Attribute finalizer if (tree.finalizer != null) attribStat(tree.finalizer, localEnv); result = null; } finally { localEnv.info.scope.leave(); } } void checkAutoCloseable(DiagnosticPosition pos, Env env, Type resource) { if (!resource.isErroneous() && types.asSuper(resource, syms.autoCloseableType.tsym) != null && !types.isSameType(resource, syms.autoCloseableType)) { // Don't emit warning for AutoCloseable itself Symbol close = syms.noSymbol; Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log); try { close = rs.resolveQualifiedMethod(pos, env, types.skipTypeVars(resource, false), names.close, List.nil(), List.nil()); } finally { log.popDiagnosticHandler(discardHandler); } if (close.kind == MTH && close.overrides(syms.autoCloseableClose, resource.tsym, types, true) && chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes()) && env.info.lint.isEnabled(LintCategory.TRY)) { log.warning(LintCategory.TRY, pos, Warnings.TryResourceThrowsInterruptedExc(resource)); } } } public void visitConditional(JCConditional tree) { Type condtype = attribExpr(tree.cond, env, syms.booleanType); tree.polyKind = (!allowPoly || pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly || isBooleanOrNumeric(env, tree)) ? PolyKind.STANDALONE : PolyKind.POLY; if (tree.polyKind == PolyKind.POLY && resultInfo.pt.hasTag(VOID)) { //this means we are returning a poly conditional from void-compatible lambda expression resultInfo.checkContext.report(tree, diags.fragment(Fragments.ConditionalTargetCantBeVoid)); result = tree.type = types.createErrorType(resultInfo.pt); return; } ResultInfo condInfo = tree.polyKind == PolyKind.STANDALONE ? unknownExprInfo : resultInfo.dup(conditionalContext(resultInfo.checkContext)); Type truetype = attribTree(tree.truepart, env, condInfo); Type falsetype = attribTree(tree.falsepart, env, condInfo); Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(List.of(tree.truepart.pos(), tree.falsepart.pos()), List.of(truetype, falsetype)) : pt(); if (condtype.constValue() != null && truetype.constValue() != null && falsetype.constValue() != null && !owntype.hasTag(NONE)) { //constant folding owntype = cfolder.coerce(condtype.isTrue() ? truetype : falsetype, owntype); } result = check(tree, owntype, KindSelector.VAL, resultInfo); } //where private boolean isBooleanOrNumeric(Env env, JCExpression tree) { switch (tree.getTag()) { case LITERAL: return ((JCLiteral)tree).typetag.isSubRangeOf(DOUBLE) || ((JCLiteral)tree).typetag == BOOLEAN || ((JCLiteral)tree).typetag == BOT; case LAMBDA: case REFERENCE: return false; case PARENS: return isBooleanOrNumeric(env, ((JCParens)tree).expr); case CONDEXPR: JCConditional condTree = (JCConditional)tree; return isBooleanOrNumeric(env, condTree.truepart) && isBooleanOrNumeric(env, condTree.falsepart); case APPLY: JCMethodInvocation speculativeMethodTree = (JCMethodInvocation)deferredAttr.attribSpeculative( tree, env, unknownExprInfo, argumentAttr.withLocalCacheContext()); Symbol msym = TreeInfo.symbol(speculativeMethodTree.meth); Type receiverType = speculativeMethodTree.meth.hasTag(IDENT) ? env.enclClass.type : ((JCFieldAccess)speculativeMethodTree.meth).selected.type; Type owntype = types.memberType(receiverType, msym).getReturnType(); return primitiveOrBoxed(owntype); case NEWCLASS: JCExpression className = removeClassParams.translate(((JCNewClass)tree).clazz); JCExpression speculativeNewClassTree = (JCExpression)deferredAttr.attribSpeculative( className, env, unknownTypeInfo, argumentAttr.withLocalCacheContext()); return primitiveOrBoxed(speculativeNewClassTree.type); default: Type speculativeType = deferredAttr.attribSpeculative(tree, env, unknownExprInfo, argumentAttr.withLocalCacheContext()).type; return primitiveOrBoxed(speculativeType); } } //where boolean primitiveOrBoxed(Type t) { return (!t.hasTag(TYPEVAR) && types.unboxedTypeOrType(t).isPrimitive()); } TreeTranslator removeClassParams = new TreeTranslator() { @Override public void visitTypeApply(JCTypeApply tree) { result = translate(tree.clazz); } }; CheckContext conditionalContext(CheckContext checkContext) { return new Check.NestedCheckContext(checkContext) { //this will use enclosing check context to check compatibility of //subexpression against target type; if we are in a method check context, //depending on whether boxing is allowed, we could have incompatibilities @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { enclosingContext.report(pos, diags.fragment(Fragments.IncompatibleTypeInConditional(details))); } }; } /** Compute the type of a conditional expression, after * checking that it exists. See JLS 15.25. 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 thentype The type of the expression's then-part. * @param elsetype The type of the expression's else-part. */ Type condType(List positions, List condTypes) { if (condTypes.isEmpty()) { return syms.objectType; //TODO: how to handle? } if (condTypes.size() == 1) { return condTypes.head; } Type first = condTypes.head; // If same type, that is the result if (condTypes.tail.stream().allMatch(t -> types.isSameType(first, t))) return first.baseType(); List unboxedTypes = condTypes.stream() .map(t -> t.isPrimitive() ? t : types.unboxedType(t)) .collect(List.collector()); // 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 (unboxedTypes.stream().allMatch(t -> t.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. for (Type type : unboxedTypes) { if (!type.getTag().isStrictSubRangeOf(INT)) { continue; } if (unboxedTypes.stream().filter(t -> t != type).allMatch(t -> t.hasTag(INT) && types.isAssignable(t, type))) return type.baseType(); } for (TypeTag tag : primitiveTags) { Type candidate = syms.typeOfTag[tag.ordinal()]; if (unboxedTypes.stream().allMatch(t -> types.isSubtype(t, candidate))) { return candidate; } } } // Those were all the cases that could result in a primitive condTypes = condTypes.stream() .map(t -> t.isPrimitive() ? types.boxedClass(t).type : t) .collect(List.collector()); for (Type type : condTypes) { if (condTypes.stream().filter(t -> t != type).allMatch(t -> types.isAssignable(t, type))) return type.baseType(); } Iterator posIt = positions.iterator(); condTypes = condTypes.stream() .map(t -> chk.checkNonVoid(posIt.next(), t)) .collect(List.collector()); // both are known to be reference types. The result is // lub(thentype,elsetype). This cannot fail, as it will // always be possible to infer "Object" if nothing better. return types.lub(condTypes.stream().map(t -> t.baseType()).collect(List.collector())); } final static TypeTag[] primitiveTags = new TypeTag[]{ BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, BOOLEAN, }; public void visitIf(JCIf tree) { attribExpr(tree.cond, env, syms.booleanType); attribStat(tree.thenpart, env); if (tree.elsepart != null) attribStat(tree.elsepart, env); chk.checkEmptyIf(tree); result = null; } public void visitExec(JCExpressionStatement tree) { //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() Env localEnv = env.dup(tree); attribExpr(tree.expr, localEnv); result = null; } public void visitBreak(JCBreak tree) { if (env.info.breakResult != null) { if (tree.value == null) { tree.target = findJumpTarget(tree.pos(), tree.getTag(), null, env); if (tree.target.hasTag(SWITCH_EXPRESSION)) { log.error(tree.pos(), Errors.BreakMissingValue); } } else { if (env.info.breakResult.pt.hasTag(VOID)) { //can happen? env.info.breakResult.checkContext.report(tree.value.pos(), diags.fragment(Fragments.UnexpectedRetVal)); } boolean attribute = true; if (tree.value.hasTag(IDENT)) { //disambiguate break