< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Print this page
rev 51258 : imported patch switch.diff

*** 24,37 **** --- 24,40 ---- */ 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;
*** 1393,1457 **** attribStat(tree.body, env.dup(tree)); result = null; } public void visitSwitch(JCSwitch tree) { ! Type seltype = attribExpr(tree.selector, env); Env<AttrContext> switchEnv = ! env.dup(tree, 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(tree.selector.pos(), seltype, syms.intType); // Attribute all cases and // check that there are no duplicate case labels or default clauses. Set<Object> labels = new HashSet<>(); // The set of case labels. boolean hasDefault = false; // Is there a default label? ! for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { JCCase c = l.head; ! if (c.pat != null) { ! if (enumSwitch) { ! Symbol sym = enumConstant(c.pat, seltype); if (sym == null) { ! log.error(c.pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); } else if (!labels.add(sym)) { log.error(c.pos(), Errors.DuplicateCaseLabel); } } else { ! Type pattype = attribExpr(c.pat, switchEnv, seltype); if (!pattype.hasTag(ERROR)) { if (pattype.constValue() == null) { ! log.error(c.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<AttrContext> caseEnv = switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup())); try { ! attribStats(c.stats, caseEnv); } finally { caseEnv.info.scope.leave(); addVars(c.stats, switchEnv.info.scope); } } ! ! result = null; ! } ! finally { switchEnv.info.scope.leave(); } } // where /** Add any variables defined in stats to the switch scope. */ --- 1396,1539 ---- 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<DiagnosticPosition> caseTypePositions = new ListBuffer<>(); ! ListBuffer<Type> 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<JCCase> cases, ! BiConsumer<JCCase, Env<AttrContext>> attribCase) { ! Type seltype = attribExpr(selector, env); Env<AttrContext> 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<Object> 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<JCCase> 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<AttrContext> 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. */
*** 1607,1617 **** 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(tree, truetype, falsetype) : pt(); if (condtype.constValue() != null && truetype.constValue() != null && falsetype.constValue() != null && !owntype.hasTag(NONE)) { //constant folding --- 1689,1701 ---- 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
*** 1688,1758 **** * @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(DiagnosticPosition pos, ! Type thentype, Type elsetype) { // If same type, that is the result ! if (types.isSameType(thentype, elsetype)) ! return thentype.baseType(); ! Type thenUnboxed = (thentype.isPrimitive()) ! ? thentype : types.unboxedType(thentype); ! Type elseUnboxed = (elsetype.isPrimitive()) ! ? elsetype : types.unboxedType(elsetype); // 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.getTag().isStrictSubRangeOf(INT) && ! elseUnboxed.hasTag(INT) && ! types.isAssignable(elseUnboxed, thenUnboxed)) { ! return thenUnboxed.baseType(); ! } ! if (elseUnboxed.getTag().isStrictSubRangeOf(INT) && ! thenUnboxed.hasTag(INT) && ! types.isAssignable(thenUnboxed, elseUnboxed)) { ! return elseUnboxed.baseType(); } for (TypeTag tag : primitiveTags) { Type candidate = syms.typeOfTag[tag.ordinal()]; ! if (types.isSubtype(thenUnboxed, candidate) && ! types.isSubtype(elseUnboxed, candidate)) { return candidate; } } } // Those were all the cases that could result in a primitive ! if (thentype.isPrimitive()) ! thentype = types.boxedClass(thentype).type; ! if (elsetype.isPrimitive()) ! elsetype = types.boxedClass(elsetype).type; ! ! if (types.isSubtype(thentype, elsetype)) ! return elsetype.baseType(); ! if (types.isSubtype(elsetype, thentype)) ! return thentype.baseType(); ! if (thentype.hasTag(VOID) || elsetype.hasTag(VOID)) { ! log.error(pos, ! Errors.NeitherConditionalSubtype(thentype, ! elsetype)); ! return thentype.baseType(); } // 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(thentype.baseType(), elsetype.baseType()); } final static TypeTag[] primitiveTags = new TypeTag[]{ BYTE, CHAR, --- 1772,1841 ---- * @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<DiagnosticPosition> positions, List<Type> 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<Type> 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<DiagnosticPosition> 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,
*** 1780,1790 **** attribExpr(tree.expr, localEnv); result = null; } public void visitBreak(JCBreak tree) { ! tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env); result = null; } public void visitContinue(JCContinue tree) { tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env); --- 1863,1934 ---- 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 <LABEL> and break <ident-as-an-expression>: ! Name label = ((JCIdent) tree.value).name; ! Pair<JCTree, Error> jumpTarget = findJumpTargetNoError(tree.getTag(), label, env); ! ! if (jumpTarget.fst != null) { ! JCTree speculative = deferredAttr.attribSpeculative(tree.value, env, unknownExprInfo); ! if (!speculative.type.hasTag(ERROR)) { ! log.error(tree.pos(), Errors.BreakAmbiguousTarget(label)); ! if (jumpTarget.snd == null) { ! tree.target = jumpTarget.fst; ! attribute = false; ! } else { ! //nothing ! } ! } else { ! if (jumpTarget.snd != null) { ! log.error(tree.pos(), jumpTarget.snd); ! } ! tree.target = jumpTarget.fst; ! attribute = false; ! } ! } ! } ! if (attribute) { ! attribTree(tree.value, env, env.info.breakResult); ! JCTree immediateTarget = findJumpTarget(tree.pos(), tree.getTag(), null, env); ! if (immediateTarget.getTag() != SWITCH_EXPRESSION) { ! log.error(tree.pos(), Errors.BreakExprNotImmediate(immediateTarget.getTag())); ! Env<AttrContext> env1 = env; ! while (env1 != null && env1.tree.getTag() != SWITCH_EXPRESSION) { ! env1 = env1.next; ! } ! Assert.checkNonNull(env1); ! tree.target = env1.tree; ! } else { ! tree.target = immediateTarget; ! } ! } ! } ! } else { ! if (tree.value == null || tree.value.hasTag(IDENT)) { ! Name label = tree.value != null ? ((JCIdent) tree.value).name : null; ! tree.target = findJumpTarget(tree.pos(), tree.getTag(), label, env); ! } else { ! log.error(tree.pos(), Errors.BreakComplexValueNoSwitchExpression); ! attribTree(tree.value, env, unknownExprInfo); ! } ! } result = null; } public void visitContinue(JCContinue tree) { tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env);
*** 1806,1817 **** --- 1950,1985 ---- */ private JCTree findJumpTarget(DiagnosticPosition pos, JCTree.Tag tag, Name label, Env<AttrContext> env) { + Pair<JCTree, Error> jumpTarget = findJumpTargetNoError(tag, label, env); + + if (jumpTarget.snd != null) { + log.error(pos, jumpTarget.snd); + } + + return jumpTarget.fst; + } + /** 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 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 Pair<JCTree, JCDiagnostic.Error> findJumpTargetNoError(JCTree.Tag tag, + Name label, + Env<AttrContext> env) { // Search environments outwards from the point of jump. Env<AttrContext> env1 = env; + JCDiagnostic.Error pendingError = null; LOOP: while (env1 != null) { switch (env1.tree.getTag()) { case LABELLED: JCLabeledStatement labelled = (JCLabeledStatement)env1.tree;
*** 1819,1869 **** // If jump is a continue, check that target is a loop. if (tag == CONTINUE) { if (!labelled.body.hasTag(DOLOOP) && !labelled.body.hasTag(WHILELOOP) && !labelled.body.hasTag(FORLOOP) && ! !labelled.body.hasTag(FOREACHLOOP)) ! log.error(pos, Errors.NotLoopLabel(label)); // Found labelled statement target, now go inwards // to next non-labelled tree. ! return TreeInfo.referencedStatement(labelled); } else { ! return labelled; } } break; case DOLOOP: case WHILELOOP: case FORLOOP: case FOREACHLOOP: ! if (label == null) return env1.tree; break; case SWITCH: ! if (label == null && tag == BREAK) return env1.tree; break; case LAMBDA: case METHODDEF: case CLASSDEF: break LOOP; default: } env1 = env1.next; } if (label != null) ! log.error(pos, Errors.UndefLabel(label)); else if (tag == CONTINUE) ! log.error(pos, Errors.ContOutsideLoop); else ! log.error(pos, Errors.BreakOutsideSwitchLoop); ! return null; } public void visitReturn(JCReturn tree) { // Check that there is an enclosing method which is // nested within than the enclosing class. if (env.info.returnResult == null) { log.error(tree.pos(), Errors.RetOutsideMeth); } else { // Attribute return expression, if it exists, and check that // it conforms to result type of enclosing method. if (tree.expr != null) { if (env.info.returnResult.pt.hasTag(VOID)) { --- 1987,2050 ---- // If jump is a continue, check that target is a loop. if (tag == CONTINUE) { if (!labelled.body.hasTag(DOLOOP) && !labelled.body.hasTag(WHILELOOP) && !labelled.body.hasTag(FORLOOP) && ! !labelled.body.hasTag(FOREACHLOOP)) { ! pendingError = Errors.NotLoopLabel(label); ! } // Found labelled statement target, now go inwards // to next non-labelled tree. ! return Pair.of(TreeInfo.referencedStatement(labelled), pendingError); } else { ! return Pair.of(labelled, pendingError); } } break; case DOLOOP: case WHILELOOP: case FORLOOP: case FOREACHLOOP: ! if (label == null) return Pair.of(env1.tree, pendingError); break; case SWITCH: ! if (label == null && tag == BREAK) return Pair.of(env1.tree, null); ! break; ! case SWITCH_EXPRESSION: ! if (tag == BREAK) { ! if (label == null) { ! return Pair.of(env1.tree, null); ! } else { ! pendingError = Errors.BreakOutsideSwitchExpression; ! } ! } else { ! pendingError = Errors.ContinueOutsideSwitchExpression; ! } break; case LAMBDA: case METHODDEF: case CLASSDEF: break LOOP; default: } env1 = env1.next; } if (label != null) ! return Pair.of(null, Errors.UndefLabel(label)); else if (tag == CONTINUE) ! return Pair.of(null, Errors.ContOutsideLoop); else ! return Pair.of(null, Errors.BreakOutsideSwitchLoop); } public void visitReturn(JCReturn tree) { // Check that there is an enclosing method which is // nested within than the enclosing class. if (env.info.returnResult == null) { log.error(tree.pos(), Errors.RetOutsideMeth); + } else if (env.info.breakResult != null) { + log.error(tree.pos(), Errors.ReturnOutsideSwitchExpression); } else { // Attribute return expression, if it exists, and check that // it conforms to result type of enclosing method. if (tree.expr != null) { if (env.info.returnResult.pt.hasTag(VOID)) {
*** 2942,2951 **** --- 3123,3133 ---- } lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared(newScopeOwner))); } else { lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup())); } + lambdaEnv.info.breakResult = null; return lambdaEnv; } @Override public void visitReference(final JCMemberReference that) {
< prev index next >