diff -r 88502b1cf76f -r 7799a51dbe30 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 09 11:43:16 2019 -0400 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 27 09:00:01 2019 +0100 @@ -49,6 +49,7 @@ 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.comp.MatchBindingsComputer.BindingSymbol; 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; @@ -110,6 +111,7 @@ final Enter enter; final Target target; final Types types; + final Preview preview; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; final DeferredLintHandler deferredLintHandler; @@ -117,6 +119,7 @@ final Dependencies dependencies; final Annotate annotate; final ArgumentAttr argumentAttr; + final MatchBindingsComputer matchBindingsComputer; public static Attr instance(Context context) { Attr instance = context.get(attrKey); @@ -145,6 +148,7 @@ cfolder = ConstFold.instance(context); target = Target.instance(context); types = Types.instance(context); + preview = Preview.instance(context); diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); @@ -152,6 +156,7 @@ typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); + matchBindingsComputer = MatchBindingsComputer.instance(context); Options options = Options.instance(context); @@ -161,6 +166,9 @@ allowLambda = Feature.LAMBDA.allowedInSource(source); allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source); allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source); + allowReifiableTypesInInstanceof = + Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) && + (!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled()); sourceName = source.name; useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); @@ -193,6 +201,10 @@ */ boolean allowStaticInterfaceMethods; + /** Switch: reifiable types in instanceof enabled? + */ + boolean allowReifiableTypesInInstanceof; + /** * Switch: warn about use of variable before declaration? * RFE: 6425594 @@ -292,6 +304,8 @@ isAssignableAsBlankFinal(v, env)))) { if (v.isResourceVariable()) { //TWR resource log.error(pos, Errors.TryResourceMayNotBeAssigned(v)); + } else if ((v.flags() & MATCH_BINDING) != 0) { + log.error(pos, Errors.PatternBindingMayNotBeAssigned(v)); } else { log.error(pos, Errors.CantAssignValToFinalVar(v)); } @@ -1298,29 +1312,73 @@ public void visitDoLoop(JCDoWhileLoop tree) { attribStat(tree.body, env.dup(tree)); attribExpr(tree.cond, env, syms.booleanType); + if (!breaksOutOf(tree, tree.body)) { + //include condition's body when false after the while, if cannot get out of the loop + List bindings = matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } public void visitWhileLoop(JCWhileLoop tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.body, env.dup(tree)); + // include condition's bindings when true in the body: + Env whileEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true)); + try { + attribStat(tree.body, whileEnv.dup(tree)); + } finally { + whileEnv.info.scope.leave(); + } + if (!breaksOutOf(tree, tree.body)) { + //include condition's bindings when false after the while, if cannot get out of the loop + List bindings = + matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } + private boolean breaksOutOf(JCTree loop, JCTree body) { + preFlow(body); + return flow.breaksOutOf(env, loop, body, make); + } + 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); + List matchBindings = List.nil(); + if (tree.cond != null) { + attribExpr(tree.cond, loopEnv, syms.booleanType); + // include condition's bindings when true in the body and step: + matchBindings = matchBindingsComputer.getMatchBindings(tree.cond, true); + } + Env bodyEnv = bindingEnv(loopEnv, matchBindings); + try { + bodyEnv.tree = tree; // before, we were not in loop! + attribStats(tree.step, bodyEnv); + attribStat(tree.body, bodyEnv); + } finally { + bodyEnv.info.scope.leave(); + } result = null; } finally { loopEnv.info.scope.leave(); } + if (!breaksOutOf(tree, tree.body)) { + //include condition's body when false after the while, if cannot get out of the loop + List bindings = + matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } } public void visitForeachLoop(JCEnhancedForLoop tree) { @@ -1673,8 +1731,26 @@ unknownExprInfo : resultInfo.dup(conditionalContext(resultInfo.checkContext)); - Type truetype = attribTree(tree.truepart, env, condInfo); - Type falsetype = attribTree(tree.falsepart, env, condInfo); + + // x ? y : z + // include x's bindings when true in y + // include x's bindings when false in z + + Type truetype; + Env trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true)); + try { + truetype = attribTree(tree.truepart, trueEnv, condInfo); + } finally { + trueEnv.info.scope.leave(); + } + + Type falsetype; + Env falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, false)); + try { + falsetype = attribTree(tree.falsepart, falseEnv, condInfo); + } finally { + falseEnv.info.scope.leave(); + } Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(List.of(tree.truepart.pos(), tree.falsepart.pos()), @@ -1829,15 +1905,77 @@ BOOLEAN, }; + Env bindingEnv(Env env, List bindings) { + Env env1 = env.dup(env.tree, env.info.dup(env.info.scope.dup())); + bindings.forEach(env1.info.scope::enter); + return env1; + } + public void visitIf(JCIf tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.thenpart, env); - if (tree.elsepart != null) - attribStat(tree.elsepart, env); + + // if (x) { y } [ else z ] + // include x's bindings when true in y + // include x's bindings when false in z + + List thenBindings = matchBindingsComputer.getMatchBindings(tree.cond, true); + Env thenEnv = bindingEnv(env, thenBindings); + + try { + attribStat(tree.thenpart, thenEnv); + } finally { + thenEnv.info.scope.leave(); + } + + preFlow(tree.thenpart); + boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make); + boolean aliveAfterElse; + List elseBindings = matchBindingsComputer.getMatchBindings(tree.cond, false); + + if (tree.elsepart != null) { + Env elseEnv = bindingEnv(env, elseBindings); + try { + attribStat(tree.elsepart, elseEnv); + } finally { + elseEnv.info.scope.leave(); + } + preFlow(tree.elsepart); + aliveAfterElse = flow.aliveAfter(env, tree.elsepart, make); + } else { + aliveAfterElse = true; + } + chk.checkEmptyIf(tree); + + List afterIfBindings = List.nil(); + + if (aliveAfterThen && !aliveAfterElse) { + afterIfBindings = thenBindings; + } else if (aliveAfterElse && !aliveAfterThen) { + afterIfBindings = elseBindings; + } + + afterIfBindings.forEach(env.info.scope::enter); + afterIfBindings.forEach(BindingSymbol::preserveBinding); + result = null; } + void preFlow(JCTree tree) { + new PostAttrAnalyzer() { + @Override + public void scan(JCTree tree) { + if (tree == null || + (tree.type != null && + tree.type == Type.stuckType)) { + //don't touch stuck expressions! + return; + } + super.scan(tree); + } + }.scan(tree); + } + public void visitExec(JCExpressionStatement tree) { //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() @@ -3521,7 +3659,32 @@ public void visitBinary(JCBinary 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)); + // x && y + // include x's bindings when true in y + + // x || y + // include x's bindings when false in y + + List matchBindings; + switch (tree.getTag()) { + case AND: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true); + break; + case OR: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false); + break; + default: + matchBindings = List.nil(); + break; + } + Env rhsEnv = bindingEnv(env, matchBindings); + Type right; + try { + right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv)); + } finally { + rhsEnv.info.scope.leave(); + } + // Find operator. Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right); Type owntype = types.createErrorType(tree.type); @@ -3587,19 +3750,63 @@ public void visitTypeTest(JCInstanceOf tree) { Type exprtype = chk.checkNullOrRefType( tree.expr.pos(), attribExpr(tree.expr, env)); - Type clazztype = attribType(tree.clazz, env); + Type clazztype; + JCTree typeTree; + if (tree.pattern.getTag() == BINDINGPATTERN) { + attribTree(tree.pattern, env, unknownExprInfo); + clazztype = tree.pattern.type; + JCBindingPattern pattern = (JCBindingPattern) tree.pattern; + typeTree = pattern.vartype; + if (!clazztype.hasTag(TYPEVAR)) { + clazztype = chk.checkClassOrArrayType(pattern.vartype.pos(), clazztype); + } + } else { + clazztype = attribType(tree.pattern, env); + typeTree = tree.pattern; + } if (!clazztype.hasTag(TYPEVAR)) { - clazztype = chk.checkClassOrArrayType(tree.clazz.pos(), clazztype); + clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype); } if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) { - log.error(tree.clazz.pos(), Errors.IllegalGenericTypeForInstof); - clazztype = types.createErrorType(clazztype); - } - chk.validate(tree.clazz, env, false); + boolean valid = false; + if (allowReifiableTypesInInstanceof) { + if (preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF)) { + preview.warnPreview(tree.expr.pos(), Feature.REIFIABLE_TYPES_INSTANCEOF); + } + Warner warner = new Warner(); + if (!types.isCastable(exprtype, clazztype, warner)) { + chk.basicHandler.report(tree.expr.pos(), + diags.fragment(Fragments.InconvertibleTypes(exprtype, clazztype))); + } else if (warner.hasLint(LintCategory.UNCHECKED)) { + log.error(tree.expr.pos(), + Errors.InstanceofReifiableNotSafe(exprtype, clazztype)); + } else { + valid = true; + } + } else { + log.error(typeTree.pos(), Errors.IllegalGenericTypeForInstof); + } + if (!valid) { + clazztype = types.createErrorType(clazztype); + } + } + chk.validate(typeTree, env, false); chk.checkCastable(tree.expr.pos(), exprtype, clazztype); result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo); } + public void visitBindingPattern(JCBindingPattern tree) { + ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext); + tree.type = attribTree(tree.vartype, env, varInfo); + VarSymbol v = tree.symbol = new BindingSymbol(tree.name, tree.vartype.type, env.info.scope.owner); + if (chk.checkUnique(tree.pos(), v, env.info.scope)) { + chk.checkTransparentVar(tree.pos(), v, env.info.scope); + } + annotate.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos()); + annotate.flush(); + result = tree.type; + } + public void visitIndexed(JCArrayAccess tree) { Type owntype = types.createErrorType(tree.type); Type atype = attribExpr(tree.indexed, env); @@ -4991,8 +5198,8 @@ super.visitTypeCast(tree); } public void visitTypeTest(JCInstanceOf tree) { - if (tree.clazz != null && tree.clazz.type != null) - validateAnnotatedType(tree.clazz, tree.clazz.type); + if (tree.pattern != null && !(tree.pattern instanceof JCPattern) && tree.pattern.type != null) + validateAnnotatedType(tree.pattern, tree.pattern.type); super.visitTypeTest(tree); } public void visitNewClass(JCNewClass tree) { @@ -5253,6 +5460,15 @@ } @Override + public void visitBindingPattern(JCBindingPattern that) { + if (that.symbol == null) { + that.symbol = new BindingSymbol(that.name, that.type, syms.noSymbol); + that.symbol.adr = 0; + } + super.visitBindingPattern(that); + } + + @Override public void visitNewClass(JCNewClass that) { if (that.constructor == null) { that.constructor = new MethodSymbol(0, names.init,