mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8236670: Conflicting bindings accepted in some cases
Reviewed-by: mcimadamore
This commit is contained in:
parent
eccf39b295
commit
a35d087f54
13 changed files with 460 additions and 222 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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
|
||||
|
@ -228,7 +228,8 @@ public class Flags {
|
|||
public static final long EFFECTIVELY_FINAL = 1L<<41;
|
||||
|
||||
/**
|
||||
* Flag that marks non-override equivalent methods with the same signature.
|
||||
* Flag that marks non-override equivalent methods with the same signature,
|
||||
* or a conflicting match binding (BindingSymbol).
|
||||
*/
|
||||
public static final long CLASH = 1L<<42;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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
|
||||
|
@ -1785,6 +1785,29 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
|||
|
||||
}
|
||||
|
||||
public static class BindingSymbol extends VarSymbol {
|
||||
|
||||
public BindingSymbol(Name name, Type type, Symbol owner) {
|
||||
super(Flags.FINAL | Flags.HASINIT | Flags.MATCH_BINDING, name, type, owner);
|
||||
}
|
||||
|
||||
public boolean isAliasFor(BindingSymbol b) {
|
||||
return aliases().containsAll(b.aliases());
|
||||
}
|
||||
|
||||
List<BindingSymbol> aliases() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
public void preserveBinding() {
|
||||
flags_field |= Flags.MATCH_BINDING_TO_OUTER;
|
||||
}
|
||||
|
||||
public boolean isPreserved() {
|
||||
return (flags_field & Flags.MATCH_BINDING_TO_OUTER) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** A class for method symbols.
|
||||
*/
|
||||
public static class MethodSymbol extends Symbol implements ExecutableElement {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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
|
||||
|
@ -49,7 +49,7 @@ 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.comp.MatchBindingsComputer.BindingSymbol;
|
||||
import com.sun.tools.javac.comp.MatchBindingsComputer.MatchBindings;
|
||||
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;
|
||||
|
@ -642,6 +642,8 @@ public class Attr extends JCTree.Visitor {
|
|||
*/
|
||||
Type result;
|
||||
|
||||
MatchBindings matchBindings = MatchBindingsComputer.EMPTY;
|
||||
|
||||
/** Visitor method: attribute a tree, catching any completion failure
|
||||
* exceptions. Return the tree's type.
|
||||
*
|
||||
|
@ -660,6 +662,8 @@ public class Attr extends JCTree.Visitor {
|
|||
} else {
|
||||
tree.accept(this);
|
||||
}
|
||||
matchBindings = matchBindingsComputer.finishBindings(tree,
|
||||
matchBindings);
|
||||
if (tree == breakTree &&
|
||||
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
|
||||
breakTreeFound(copyEnv(env));
|
||||
|
@ -1418,18 +1422,18 @@ public class Attr extends JCTree.Visitor {
|
|||
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<BindingSymbol> bindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
|
||||
|
||||
bindings.forEach(env.info.scope::enter);
|
||||
bindings.forEach(BindingSymbol::preserveBinding);
|
||||
MatchBindings condBindings = matchBindings;
|
||||
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
|
||||
public void visitWhileLoop(JCWhileLoop tree) {
|
||||
attribExpr(tree.cond, env, syms.booleanType);
|
||||
MatchBindings condBindings = matchBindings;
|
||||
// include condition's bindings when true in the body:
|
||||
Env<AttrContext> whileEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
|
||||
Env<AttrContext> whileEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
|
||||
try {
|
||||
attribStat(tree.body, whileEnv.dup(tree));
|
||||
} finally {
|
||||
|
@ -1437,11 +1441,8 @@ public class Attr extends JCTree.Visitor {
|
|||
}
|
||||
if (!breaksOutOf(tree, tree.body)) {
|
||||
//include condition's bindings when false after the while, if cannot get out of the loop
|
||||
List<BindingSymbol> bindings =
|
||||
matchBindingsComputer.getMatchBindings(tree.cond, false);
|
||||
|
||||
bindings.forEach(env.info.scope::enter);
|
||||
bindings.forEach(BindingSymbol::preserveBinding);
|
||||
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
|
@ -1454,15 +1455,15 @@ public class Attr extends JCTree.Visitor {
|
|||
public void visitForLoop(JCForLoop tree) {
|
||||
Env<AttrContext> loopEnv =
|
||||
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
|
||||
MatchBindings condBindings = MatchBindingsComputer.EMPTY;
|
||||
try {
|
||||
attribStats(tree.init, loopEnv);
|
||||
List<BindingSymbol> 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);
|
||||
condBindings = matchBindings;
|
||||
}
|
||||
Env<AttrContext> bodyEnv = bindingEnv(loopEnv, matchBindings);
|
||||
Env<AttrContext> bodyEnv = bindingEnv(loopEnv, condBindings.bindingsWhenTrue);
|
||||
try {
|
||||
bodyEnv.tree = tree; // before, we were not in loop!
|
||||
attribStats(tree.step, bodyEnv);
|
||||
|
@ -1477,11 +1478,8 @@ public class Attr extends JCTree.Visitor {
|
|||
}
|
||||
if (!breaksOutOf(tree, tree.body)) {
|
||||
//include condition's body when false after the while, if cannot get out of the loop
|
||||
List<BindingSymbol> bindings =
|
||||
matchBindingsComputer.getMatchBindings(tree.cond, false);
|
||||
|
||||
bindings.forEach(env.info.scope::enter);
|
||||
bindings.forEach(BindingSymbol::preserveBinding);
|
||||
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1818,6 +1816,7 @@ public class Attr extends JCTree.Visitor {
|
|||
|
||||
public void visitConditional(JCConditional tree) {
|
||||
Type condtype = attribExpr(tree.cond, env, syms.booleanType);
|
||||
MatchBindings condBindings = matchBindings;
|
||||
|
||||
tree.polyKind = (!allowPoly ||
|
||||
pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly ||
|
||||
|
@ -1841,21 +1840,25 @@ public class Attr extends JCTree.Visitor {
|
|||
// include x's bindings when false in z
|
||||
|
||||
Type truetype;
|
||||
Env<AttrContext> trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
|
||||
Env<AttrContext> trueEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
|
||||
try {
|
||||
truetype = attribTree(tree.truepart, trueEnv, condInfo);
|
||||
} finally {
|
||||
trueEnv.info.scope.leave();
|
||||
}
|
||||
|
||||
MatchBindings trueBindings = matchBindings;
|
||||
|
||||
Type falsetype;
|
||||
Env<AttrContext> falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, false));
|
||||
Env<AttrContext> falseEnv = bindingEnv(env, condBindings.bindingsWhenFalse);
|
||||
try {
|
||||
falsetype = attribTree(tree.falsepart, falseEnv, condInfo);
|
||||
} finally {
|
||||
falseEnv.info.scope.leave();
|
||||
}
|
||||
|
||||
MatchBindings falseBindings = matchBindings;
|
||||
|
||||
Type owntype = (tree.polyKind == PolyKind.STANDALONE) ?
|
||||
condType(List.of(tree.truepart.pos(), tree.falsepart.pos()),
|
||||
List.of(truetype, falsetype)) : pt();
|
||||
|
@ -1867,6 +1870,7 @@ public class Attr extends JCTree.Visitor {
|
|||
owntype = cfolder.coerce(condtype.isTrue() ? truetype : falsetype, owntype);
|
||||
}
|
||||
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
||||
matchBindings = matchBindingsComputer.conditional(tree, condBindings, trueBindings, falseBindings);
|
||||
}
|
||||
//where
|
||||
private boolean isBooleanOrNumeric(Env<AttrContext> env, JCExpression tree) {
|
||||
|
@ -2022,8 +2026,8 @@ public class Attr extends JCTree.Visitor {
|
|||
// include x's bindings when true in y
|
||||
// include x's bindings when false in z
|
||||
|
||||
List<BindingSymbol> thenBindings = matchBindingsComputer.getMatchBindings(tree.cond, true);
|
||||
Env<AttrContext> thenEnv = bindingEnv(env, thenBindings);
|
||||
MatchBindings condBindings = matchBindings;
|
||||
Env<AttrContext> thenEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
|
||||
|
||||
try {
|
||||
attribStat(tree.thenpart, thenEnv);
|
||||
|
@ -2034,10 +2038,9 @@ public class Attr extends JCTree.Visitor {
|
|||
preFlow(tree.thenpart);
|
||||
boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make);
|
||||
boolean aliveAfterElse;
|
||||
List<BindingSymbol> elseBindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
|
||||
|
||||
if (tree.elsepart != null) {
|
||||
Env<AttrContext> elseEnv = bindingEnv(env, elseBindings);
|
||||
Env<AttrContext> elseEnv = bindingEnv(env, condBindings.bindingsWhenFalse);
|
||||
try {
|
||||
attribStat(tree.elsepart, elseEnv);
|
||||
} finally {
|
||||
|
@ -2054,9 +2057,9 @@ public class Attr extends JCTree.Visitor {
|
|||
List<BindingSymbol> afterIfBindings = List.nil();
|
||||
|
||||
if (aliveAfterThen && !aliveAfterElse) {
|
||||
afterIfBindings = thenBindings;
|
||||
afterIfBindings = condBindings.bindingsWhenTrue;
|
||||
} else if (aliveAfterElse && !aliveAfterThen) {
|
||||
afterIfBindings = elseBindings;
|
||||
afterIfBindings = condBindings.bindingsWhenFalse;
|
||||
}
|
||||
|
||||
afterIfBindings.forEach(env.info.scope::enter);
|
||||
|
@ -3767,6 +3770,7 @@ public class Attr extends JCTree.Visitor {
|
|||
}
|
||||
}
|
||||
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
||||
matchBindings = matchBindingsComputer.unary(tree, matchBindings);
|
||||
}
|
||||
|
||||
public void visitBinary(JCBinary tree) {
|
||||
|
@ -3778,19 +3782,20 @@ public class Attr extends JCTree.Visitor {
|
|||
// x || y
|
||||
// include x's bindings when false in y
|
||||
|
||||
List<BindingSymbol> matchBindings;
|
||||
MatchBindings lhsBindings = matchBindings;
|
||||
List<BindingSymbol> propagatedBindings;
|
||||
switch (tree.getTag()) {
|
||||
case AND:
|
||||
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
|
||||
propagatedBindings = lhsBindings.bindingsWhenTrue;
|
||||
break;
|
||||
case OR:
|
||||
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false);
|
||||
propagatedBindings = lhsBindings.bindingsWhenFalse;
|
||||
break;
|
||||
default:
|
||||
matchBindings = List.nil();
|
||||
propagatedBindings = List.nil();
|
||||
break;
|
||||
}
|
||||
Env<AttrContext> rhsEnv = bindingEnv(env, matchBindings);
|
||||
Env<AttrContext> rhsEnv = bindingEnv(env, propagatedBindings);
|
||||
Type right;
|
||||
try {
|
||||
right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv));
|
||||
|
@ -3798,6 +3803,8 @@ public class Attr extends JCTree.Visitor {
|
|||
rhsEnv.info.scope.leave();
|
||||
}
|
||||
|
||||
matchBindings = matchBindingsComputer.binary(tree, lhsBindings, matchBindings);
|
||||
|
||||
// Find operator.
|
||||
Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right);
|
||||
Type owntype = types.createErrorType(tree.type);
|
||||
|
@ -3918,6 +3925,7 @@ public class Attr extends JCTree.Visitor {
|
|||
annotate.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos());
|
||||
annotate.flush();
|
||||
result = tree.type;
|
||||
matchBindings = new MatchBindings(List.of(tree.symbol), List.nil());
|
||||
}
|
||||
|
||||
public void visitIndexed(JCArrayAccess tree) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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
|
||||
|
@ -3604,7 +3604,10 @@ public class Check {
|
|||
} else if ((sym.flags() & MATCH_BINDING) != 0 &&
|
||||
(byName.flags() & MATCH_BINDING) != 0 &&
|
||||
(byName.flags() & MATCH_BINDING_TO_OUTER) == 0) {
|
||||
//this error will be reported separatelly in MatchBindingsComputer
|
||||
if (!sym.type.isErroneous()) {
|
||||
log.error(pos, Errors.MatchBindingExists);
|
||||
sym.flags_field |= CLASH;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
duplicateError(pos, byName);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020, 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
|
||||
|
@ -25,31 +25,26 @@
|
|||
|
||||
package com.sun.tools.javac.comp;
|
||||
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import com.sun.tools.javac.code.Symbol.BindingSymbol;
|
||||
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBinary;
|
||||
import com.sun.tools.javac.tree.JCTree.JCConditional;
|
||||
import com.sun.tools.javac.tree.JCTree.JCUnary;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
||||
import com.sun.tools.javac.tree.JCTree.Tag;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
|
||||
import static com.sun.tools.javac.code.Flags.CLASH;
|
||||
|
||||
|
||||
public class MatchBindingsComputer extends TreeScanner {
|
||||
public static final MatchBindings EMPTY = new MatchBindings(List.nil(), List.nil());
|
||||
|
||||
protected static final Context.Key<MatchBindingsComputer> matchBindingsComputerKey = new Context.Key<>();
|
||||
|
||||
private final Log log;
|
||||
private final Types types;
|
||||
boolean whenTrue;
|
||||
List<BindingSymbol> bindings;
|
||||
|
||||
public static MatchBindingsComputer instance(Context context) {
|
||||
MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
|
||||
|
@ -60,106 +55,99 @@ public class MatchBindingsComputer extends TreeScanner {
|
|||
|
||||
protected MatchBindingsComputer(Context context) {
|
||||
this.log = Log.instance(context);
|
||||
this.types = Types.instance(context);
|
||||
}
|
||||
|
||||
public List<BindingSymbol> getMatchBindings(JCTree expression, boolean whenTrue) {
|
||||
this.whenTrue = whenTrue;
|
||||
this.bindings = List.nil();
|
||||
scan(expression);
|
||||
return bindings;
|
||||
public MatchBindings conditional(JCTree tree, MatchBindings condBindings, MatchBindings trueBindings, MatchBindings falseBindings) {
|
||||
if (condBindings == EMPTY &&
|
||||
trueBindings == EMPTY &&
|
||||
falseBindings == EMPTY) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBindingPattern(JCBindingPattern tree) {
|
||||
bindings = whenTrue ? List.of(tree.symbol) : List.nil();
|
||||
}
|
||||
DiagnosticPosition pos = tree.pos();
|
||||
//A pattern variable is introduced both by a when true, and by c when true:
|
||||
List<BindingSymbol> xTzT = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
|
||||
//A pattern variable is introduced both by a when false, and by b when true:
|
||||
List<BindingSymbol> xFyT = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenTrue);
|
||||
//A pattern variable is introduced both by b when true, and by c when true:
|
||||
List<BindingSymbol> yTzT = intersection(pos, trueBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
|
||||
//A pattern variable is introduced both by a when true, and by c when false:
|
||||
List<BindingSymbol> xTzF = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenFalse);
|
||||
//A pattern variable is introduced both by a when false, and by b when false:
|
||||
List<BindingSymbol> xFyF = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenFalse);
|
||||
//A pattern variable is introduced both by b when false, and by c when false:
|
||||
List<BindingSymbol> yFzF = intersection(pos, trueBindings.bindingsWhenFalse, falseBindings.bindingsWhenFalse);
|
||||
|
||||
@Override
|
||||
public void visitBinary(JCBinary tree) {
|
||||
switch (tree.getTag()) {
|
||||
case AND:
|
||||
// e.T = union(x.T, y.T)
|
||||
// e.F = intersection(x.F, y.F)
|
||||
scan(tree.lhs);
|
||||
List<BindingSymbol> lhsBindings = bindings;
|
||||
scan(tree.rhs);
|
||||
List<BindingSymbol> rhsBindings = bindings;
|
||||
bindings = whenTrue ? union(tree, lhsBindings, rhsBindings) : intersection(tree, lhsBindings, rhsBindings);
|
||||
break;
|
||||
case OR:
|
||||
// e.T = intersection(x.T, y.T)
|
||||
// e.F = union(x.F, y.F)
|
||||
scan(tree.lhs);
|
||||
lhsBindings = bindings;
|
||||
scan(tree.rhs);
|
||||
rhsBindings = bindings;
|
||||
bindings = whenTrue ? intersection(tree, lhsBindings, rhsBindings) : union(tree, lhsBindings, rhsBindings);
|
||||
break;
|
||||
default:
|
||||
super.visitBinary(tree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnary(JCUnary tree) {
|
||||
switch (tree.getTag()) {
|
||||
case NOT:
|
||||
// e.T = x.F // flip 'em
|
||||
// e.F = x.T
|
||||
whenTrue = !whenTrue;
|
||||
scan(tree.arg);
|
||||
whenTrue = !whenTrue;
|
||||
break;
|
||||
default:
|
||||
super.visitUnary(tree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConditional(JCConditional tree) {
|
||||
//error recovery:
|
||||
/* if e = "x ? y : z", then:
|
||||
e.T = union(intersect(y.T, z.T), intersect(x.T, z.T), intersect(x.F, y.T))
|
||||
e.F = union(intersect(y.F, z.F), intersect(x.T, z.F), intersect(x.F, y.F))
|
||||
*/
|
||||
if (whenTrue) {
|
||||
List<BindingSymbol> xT, yT, zT, xF;
|
||||
scan(tree.cond);
|
||||
xT = bindings;
|
||||
scan(tree.truepart);
|
||||
yT = bindings;
|
||||
scan(tree.falsepart);
|
||||
zT = bindings;
|
||||
whenTrue = false;
|
||||
scan(tree.cond);
|
||||
xF = bindings;
|
||||
whenTrue = true;
|
||||
bindings = union(tree, intersection(tree, yT, zT), intersection(tree, xT, zT), intersection(tree, xF, yT));
|
||||
} else {
|
||||
List<BindingSymbol> xF, yF, zF, xT;
|
||||
scan(tree.cond);
|
||||
xF = bindings;
|
||||
scan(tree.truepart);
|
||||
yF = bindings;
|
||||
scan(tree.falsepart);
|
||||
zF = bindings;
|
||||
whenTrue = true;
|
||||
scan(tree.cond);
|
||||
xT = bindings;
|
||||
whenTrue = false;
|
||||
bindings = union(tree, intersection(tree, yF, zF), intersection(tree, xT, zF), intersection(tree, xF, yF));
|
||||
List<BindingSymbol> bindingsWhenTrue = union(pos, yTzT, xTzT, xFyT);
|
||||
List<BindingSymbol> bindingsWhenFalse = union(pos, yFzF, xTzF, xFyF);
|
||||
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
|
||||
}
|
||||
|
||||
public MatchBindings unary(JCTree tree, MatchBindings bindings) {
|
||||
if (bindings == EMPTY || !tree.hasTag(Tag.NOT)) return bindings;
|
||||
return new MatchBindings(bindings.bindingsWhenFalse, bindings.bindingsWhenTrue);
|
||||
}
|
||||
|
||||
public MatchBindings binary(JCTree tree, MatchBindings lhsBindings, MatchBindings rhsBindings) {
|
||||
switch (tree.getTag()) {
|
||||
case AND: {
|
||||
// e.T = union(x.T, y.T)
|
||||
// e.F = intersection(x.F, y.F) (error recovery)
|
||||
List<BindingSymbol> bindingsWhenTrue =
|
||||
union(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
|
||||
List<BindingSymbol> bindingsWhenFalse = //error recovery
|
||||
intersection(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
|
||||
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
|
||||
}
|
||||
case OR: {
|
||||
// e.T = intersection(x.T, y.T) (error recovery)
|
||||
// e.F = union(x.F, y.F)
|
||||
List<BindingSymbol> bindingsWhenTrue = //error recovery
|
||||
intersection(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
|
||||
List<Symbol.BindingSymbol> bindingsWhenFalse =
|
||||
union(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
|
||||
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
|
||||
}
|
||||
}
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
|
||||
switch (tree.getTag()) {
|
||||
case NOT: case AND: case OR: case BINDINGPATTERN:
|
||||
case PARENS: case TYPETEST:
|
||||
case CONDEXPR: //error recovery:
|
||||
return matchBindings;
|
||||
default:
|
||||
return MatchBindingsComputer.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
private List<BindingSymbol> intersection(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> rhsBindings) {
|
||||
public static class MatchBindings {
|
||||
|
||||
public final List<BindingSymbol> bindingsWhenTrue;
|
||||
public final List<BindingSymbol> bindingsWhenFalse;
|
||||
|
||||
public MatchBindings(List<BindingSymbol> bindingsWhenTrue, List<BindingSymbol> bindingsWhenFalse) {
|
||||
this.bindingsWhenTrue = bindingsWhenTrue;
|
||||
this.bindingsWhenFalse = bindingsWhenFalse;
|
||||
}
|
||||
|
||||
}
|
||||
private List<BindingSymbol> intersection(DiagnosticPosition pos, List<BindingSymbol> lhsBindings, List<BindingSymbol> rhsBindings) {
|
||||
// It is an error if, for intersection(a,b), if a and b contain the same variable name (may be eventually relaxed to merge variables of same type)
|
||||
List<BindingSymbol> list = List.nil();
|
||||
for (BindingSymbol v1 : lhsBindings) {
|
||||
for (BindingSymbol v2 : rhsBindings) {
|
||||
if (v1.name == v2.name) {
|
||||
log.error(tree.pos(), Errors.MatchBindingExists);
|
||||
if (v1.name == v2.name &&
|
||||
(v1.flags() & CLASH) == 0 &&
|
||||
(v2.flags() & CLASH) == 0) {
|
||||
log.error(pos, Errors.MatchBindingExists);
|
||||
list = list.append(v2);
|
||||
}
|
||||
}
|
||||
|
@ -168,14 +156,16 @@ public class MatchBindingsComputer extends TreeScanner {
|
|||
}
|
||||
|
||||
@SafeVarargs
|
||||
private final List<BindingSymbol> union(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> ... rhsBindings_s) {
|
||||
private final List<BindingSymbol> union(DiagnosticPosition pos, List<BindingSymbol> lhsBindings, List<BindingSymbol> ... rhsBindings_s) {
|
||||
// It is an error if for union(a,b), a and b contain the same name (disjoint union).
|
||||
List<BindingSymbol> list = lhsBindings;
|
||||
for (List<BindingSymbol> rhsBindings : rhsBindings_s) {
|
||||
for (BindingSymbol v : rhsBindings) {
|
||||
for (BindingSymbol ov : list) {
|
||||
if (ov.name == v.name) {
|
||||
log.error(tree.pos(), Errors.MatchBindingExists);
|
||||
if (ov.name == v.name &&
|
||||
(ov.flags() & CLASH) == 0 &&
|
||||
(v.flags() & CLASH) == 0) {
|
||||
log.error(pos, Errors.MatchBindingExists);
|
||||
}
|
||||
}
|
||||
list = list.append(v);
|
||||
|
@ -183,34 +173,4 @@ public class MatchBindingsComputer extends TreeScanner {
|
|||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scan(JCTree tree) {
|
||||
bindings = List.nil();
|
||||
super.scan(tree);
|
||||
}
|
||||
|
||||
public static class BindingSymbol extends VarSymbol {
|
||||
|
||||
public BindingSymbol(Name name, Type type, Symbol owner) {
|
||||
super(Flags.FINAL | Flags.HASINIT | Flags.MATCH_BINDING, name, type, owner);
|
||||
}
|
||||
|
||||
public boolean isAliasFor(BindingSymbol b) {
|
||||
return aliases().containsAll(b.aliases());
|
||||
}
|
||||
|
||||
List<BindingSymbol> aliases() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
public void preserveBinding() {
|
||||
flags_field |= Flags.MATCH_BINDING_TO_OUTER;
|
||||
}
|
||||
|
||||
public boolean isPreserved() {
|
||||
return (flags_field & Flags.MATCH_BINDING_TO_OUTER) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020, 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
|
||||
|
@ -26,11 +26,12 @@
|
|||
package com.sun.tools.javac.comp;
|
||||
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.BindingSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCAssign;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBinary;
|
||||
|
@ -42,7 +43,6 @@ import com.sun.tools.javac.tree.JCTree.JCIf;
|
|||
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
||||
|
@ -51,7 +51,6 @@ import com.sun.tools.javac.tree.TreeMaker;
|
|||
import com.sun.tools.javac.tree.TreeTranslator;
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Names;
|
||||
|
@ -59,18 +58,18 @@ import com.sun.tools.javac.util.Options;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
|
||||
import static com.sun.tools.javac.code.TypeTag.BOT;
|
||||
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
|
||||
import com.sun.tools.javac.jvm.Target;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBlock;
|
||||
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLambda;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.LetExpr;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This pass translates pattern-matching constructs, such as instanceof <pattern>.
|
||||
|
@ -93,10 +92,14 @@ public class TransPatterns extends TreeTranslator {
|
|||
private final ConstFold constFold;
|
||||
private final Names names;
|
||||
private final Target target;
|
||||
private final MatchBindingsComputer matchBindingsComputer;
|
||||
private TreeMaker make;
|
||||
|
||||
BindingContext bindingContext = new BindingContext() {
|
||||
@Override
|
||||
VarSymbol bindingDeclared(BindingSymbol varSymbol) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
VarSymbol getBindingFor(BindingSymbol varSymbol) {
|
||||
return null;
|
||||
|
@ -140,7 +143,6 @@ public class TransPatterns extends TreeTranslator {
|
|||
constFold = ConstFold.instance(context);
|
||||
names = Names.instance(context);
|
||||
target = Target.instance(context);
|
||||
matchBindingsComputer = MatchBindingsComputer.instance(context);
|
||||
debugTransPatterns = Options.instance(context).isSet("debug.patterns");
|
||||
}
|
||||
|
||||
|
@ -164,8 +166,8 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
|
||||
|
||||
VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol);
|
||||
if (bindingVar != null) {
|
||||
VarSymbol bindingVar = bindingContext.bindingDeclared(patt.symbol);
|
||||
if (bindingVar != null) { //TODO: cannot be null here?
|
||||
JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
|
||||
make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
|
||||
result = makeBinary(Tag.AND, (JCExpression)result,
|
||||
|
@ -180,20 +182,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitBinary(JCBinary tree) {
|
||||
List<BindingSymbol> 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;
|
||||
}
|
||||
|
||||
bindingContext = new BasicBindingContext(matchBindings);
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitBinary(tree);
|
||||
result = bindingContext.decorateExpression(tree);
|
||||
|
@ -204,9 +193,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitConditional(JCConditional tree) {
|
||||
bindingContext = new BasicBindingContext(
|
||||
matchBindingsComputer.getMatchBindings(tree.cond, true)
|
||||
.appendList(matchBindingsComputer.getMatchBindings(tree.cond, false)));
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitConditional(tree);
|
||||
result = bindingContext.decorateExpression(tree);
|
||||
|
@ -217,7 +204,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitIf(JCIf tree) {
|
||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitIf(tree);
|
||||
result = bindingContext.decorateStatement(tree);
|
||||
|
@ -228,7 +215,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitForLoop(JCForLoop tree) {
|
||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitForLoop(tree);
|
||||
result = bindingContext.decorateStatement(tree);
|
||||
|
@ -239,7 +226,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitWhileLoop(JCWhileLoop tree) {
|
||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitWhileLoop(tree);
|
||||
result = bindingContext.decorateStatement(tree);
|
||||
|
@ -250,7 +237,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
|
||||
@Override
|
||||
public void visitDoLoop(JCDoWhileLoop tree) {
|
||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
||||
bindingContext = new BasicBindingContext();
|
||||
try {
|
||||
super.visitDoLoop(tree);
|
||||
result = bindingContext.decorateStatement(tree);
|
||||
|
@ -286,7 +273,7 @@ public class TransPatterns extends TreeTranslator {
|
|||
@Override
|
||||
public void visitBlock(JCBlock tree) {
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
||||
bindingContext = new BasicBindingContext(List.nil()) {
|
||||
bindingContext = new BindingDeclarationFenceBindingContext() {
|
||||
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
|
||||
//{
|
||||
// if (E instanceof T N) {
|
||||
|
@ -319,6 +306,17 @@ public class TransPatterns extends TreeTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambda(JCLambda tree) {
|
||||
BindingContext prevContent = bindingContext;
|
||||
try {
|
||||
bindingContext = new BindingDeclarationFenceBindingContext();
|
||||
super.visitLambda(tree);
|
||||
} finally {
|
||||
bindingContext = prevContent;
|
||||
}
|
||||
}
|
||||
|
||||
public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
|
||||
try {
|
||||
this.make = make;
|
||||
|
@ -360,11 +358,8 @@ public class TransPatterns extends TreeTranslator {
|
|||
return result;
|
||||
}
|
||||
|
||||
private List<BindingSymbol> getMatchBindings(JCExpression cond) {
|
||||
return matchBindingsComputer.getMatchBindings(cond, true)
|
||||
.appendList(matchBindingsComputer.getMatchBindings(cond, false));
|
||||
}
|
||||
abstract class BindingContext {
|
||||
abstract VarSymbol bindingDeclared(BindingSymbol varSymbol);
|
||||
abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
|
||||
abstract JCStatement decorateStatement(JCStatement stat);
|
||||
abstract JCExpression decorateExpression(JCExpression expr);
|
||||
|
@ -373,20 +368,23 @@ public class TransPatterns extends TreeTranslator {
|
|||
}
|
||||
|
||||
class BasicBindingContext extends BindingContext {
|
||||
List<BindingSymbol> matchBindings;
|
||||
Map<BindingSymbol, VarSymbol> hoistedVarMap;
|
||||
BindingContext parent;
|
||||
|
||||
public BasicBindingContext(List<BindingSymbol> matchBindings) {
|
||||
this.matchBindings = matchBindings;
|
||||
public BasicBindingContext() {
|
||||
this.parent = bindingContext;
|
||||
this.hoistedVarMap = matchBindings.stream()
|
||||
.filter(v -> parent.getBindingFor(v) == null)
|
||||
.collect(Collectors.toMap(v -> v, v -> {
|
||||
VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner);
|
||||
res.setTypeAttributes(v.getRawTypeAttributes());
|
||||
this.hoistedVarMap = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
VarSymbol bindingDeclared(BindingSymbol varSymbol) {
|
||||
VarSymbol res = parent.bindingDeclared(varSymbol);
|
||||
if (res == null) {
|
||||
res = new VarSymbol(varSymbol.flags(), varSymbol.name, varSymbol.type, varSymbol.owner);
|
||||
res.setTypeAttributes(varSymbol.getRawTypeAttributes());
|
||||
hoistedVarMap.put(varSymbol, res);
|
||||
}
|
||||
return res;
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -454,4 +452,13 @@ public class TransPatterns extends TreeTranslator {
|
|||
return make.at(pos).VarDef(varSymbol, null);
|
||||
}
|
||||
}
|
||||
|
||||
private class BindingDeclarationFenceBindingContext extends BasicBindingContext {
|
||||
|
||||
@Override
|
||||
VarSymbol bindingDeclared(BindingSymbol varSymbol) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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
|
||||
|
@ -38,7 +38,6 @@ import com.sun.tools.javac.code.*;
|
|||
import com.sun.tools.javac.code.Directive.RequiresDirective;
|
||||
import com.sun.tools.javac.code.Scope.*;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.DefinedBy.Api;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
BindingsExistTest.java:9:36: compiler.err.match.binding.exists
|
||||
BindingsExistTest.java:9:61: compiler.err.match.binding.exists
|
||||
BindingsExistTest.java:11:36: compiler.err.match.binding.exists
|
||||
BindingsExistTest.java:16:35: compiler.err.already.defined: kindname.variable, k, kindname.method, t(java.lang.Object,java.lang.Object)
|
||||
BindingsExistTest.java:19:34: compiler.err.already.defined: kindname.variable, s2, kindname.method, t(java.lang.Object,java.lang.Object)
|
||||
|
|
|
@ -188,6 +188,41 @@ public class BindingsTest1 {
|
|||
boolean result = (o1 instanceof String a1) ? (o1 instanceof String a2) : (!(o1 instanceof String a3));
|
||||
boolean result2 = (o1 instanceof String a1) ? (o1 instanceof String a2) : (!(switch (0) { default -> false; }));
|
||||
|
||||
//binding in an expression lambda:
|
||||
if (!((VoidPredicate) () -> o1 instanceof String str && !str.isEmpty()).get()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
//binding in an block lambda:
|
||||
if (!((VoidPredicate) () -> o1 instanceof String str && !str.isEmpty()).get()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
//binding in an anonymous class:
|
||||
if (!new VoidPredicate() { public boolean get() { return o1 instanceof String str && !str.isEmpty();} }.get()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
if (!switch (i) {
|
||||
default:
|
||||
if (!(o1 instanceof String str)) {
|
||||
yield false;
|
||||
}
|
||||
if (str.isEmpty()) {
|
||||
yield true;
|
||||
}
|
||||
yield true;
|
||||
}) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
System.out.println("BindingsTest1 complete");
|
||||
}
|
||||
|
||||
interface VoidPredicate {
|
||||
public boolean get();
|
||||
}
|
||||
static boolean id(boolean b) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
|
139
test/langtools/tools/javac/patterns/ConditionalTest.java
Normal file
139
test/langtools/tools/javac/patterns/ConditionalTest.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2020, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8236670
|
||||
* @summary Verify proper scope of binding related to loops and breaks.
|
||||
* @library /tools/lib /tools/javac/lib
|
||||
* @modules
|
||||
* java.base/jdk.internal
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.file
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask
|
||||
* @build combo.ComboTestHelper
|
||||
* @compile --enable-preview -source ${jdk.version} ConditionalTest.java
|
||||
* @run main/othervm --enable-preview ConditionalTest
|
||||
*/
|
||||
|
||||
import combo.ComboInstance;
|
||||
import combo.ComboParameter;
|
||||
import combo.ComboTask;
|
||||
import combo.ComboTestHelper;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class ConditionalTest extends ComboInstance<ConditionalTest> {
|
||||
protected ToolBox tb;
|
||||
|
||||
ConditionalTest() {
|
||||
super();
|
||||
tb = new ToolBox();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new ComboTestHelper<ConditionalTest>()
|
||||
.withDimension("COND", (x, cond) -> x.cond = cond, Pattern.values())
|
||||
.withDimension("TRUE", (x, trueSec) -> x.trueSec = trueSec, Pattern.values())
|
||||
.withDimension("FALSE", (x, falseSec) -> x.falseSec = falseSec, Pattern.values())
|
||||
.run(ConditionalTest::new);
|
||||
}
|
||||
|
||||
private Pattern cond;
|
||||
private Pattern trueSec;
|
||||
private Pattern falseSec;
|
||||
|
||||
private static final String MAIN_TEMPLATE =
|
||||
"""
|
||||
public class Test {
|
||||
public static boolean doTest(Object o, boolean b) {
|
||||
return #{COND} ? #{TRUE} : #{FALSE}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
@Override
|
||||
protected void doWork() throws Throwable {
|
||||
Path base = Paths.get(".");
|
||||
|
||||
ComboTask task = newCompilationTask()
|
||||
.withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) {
|
||||
case "COND" -> cond;
|
||||
case "TRUE" -> trueSec;
|
||||
case "FALSE" -> falseSec;
|
||||
default -> throw new UnsupportedOperationException(pname);
|
||||
})
|
||||
.withOption("--enable-preview")
|
||||
.withOption("-source")
|
||||
.withOption(String.valueOf(Runtime.version().feature()));
|
||||
|
||||
task.analyze(result -> {
|
||||
boolean shouldPass;
|
||||
if (cond == Pattern.TRUE && (trueSec == Pattern.TRUE || trueSec == Pattern.FALSE)) { //6 cases covered
|
||||
shouldPass = false; //already in scope in true section
|
||||
} else if (cond == Pattern.FALSE && (falseSec == Pattern.TRUE || falseSec == Pattern.FALSE)) { //6 cases covered
|
||||
shouldPass = false; //already in scope in false section
|
||||
} else if (cond == Pattern.TRUE && falseSec == Pattern.TRUE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (cond == Pattern.FALSE && trueSec == Pattern.TRUE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (trueSec == Pattern.TRUE && falseSec == Pattern.TRUE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (trueSec == Pattern.TRUE && falseSec == Pattern.TRUE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (cond == Pattern.TRUE && falseSec == Pattern.FALSE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (cond == Pattern.FALSE && trueSec == Pattern.FALSE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else if (trueSec == Pattern.FALSE && falseSec == Pattern.FALSE) {
|
||||
shouldPass = false; //JLS 6.3.1.4
|
||||
} else {
|
||||
shouldPass = true;
|
||||
}
|
||||
|
||||
if (!shouldPass) {
|
||||
result.containsKey("Blabla");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public enum Pattern implements ComboParameter {
|
||||
NONE("b"),
|
||||
TRUE("o instanceof String s"),
|
||||
FALSE("!(o instanceof String s)");
|
||||
private final String code;
|
||||
|
||||
private Pattern(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expand(String optParameter) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@ public class DuplicateBindingTest {
|
|||
|
||||
int f;
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static boolean main(String[] args) {
|
||||
|
||||
if (args != null) {
|
||||
int s;
|
||||
|
@ -18,5 +18,45 @@ public class DuplicateBindingTest {
|
|||
if (args[0] instanceof String f) { // OK to redef field.
|
||||
}
|
||||
}
|
||||
|
||||
Object o1 = "";
|
||||
Object o2 = "";
|
||||
|
||||
if (o1 instanceof String s && o2 instanceof String s) {} //error - already in scope on RHS (in scope due to LHS when true)
|
||||
if (o1 instanceof String s && !(o2 instanceof String s)) {} //error - already in scope on RHS (in scope due to LHS when true)
|
||||
if (!(o1 instanceof String s) && !(o2 instanceof String s)) {} //error - the same binding variable on LHS and RHS when false
|
||||
if (!(o1 instanceof String s) && (o2 instanceof String s)) {} //ok
|
||||
|
||||
if (!(o1 instanceof String s) || o2 instanceof String s) {} //error - already in scope on RHS (in scope due to LHS when false)
|
||||
if (!(o1 instanceof String s) || !(o2 instanceof String s)) {} //error - already in scope on RHS (in scope due to LHS when false)
|
||||
if (o1 instanceof String s || o2 instanceof String s) {} //error - the same binding variable on LHS and RHS when true
|
||||
if (o1 instanceof String s || !(o2 instanceof String s)) {} //ok
|
||||
|
||||
if (o1 instanceof String s ? o2 instanceof String s : true) {} //error - already in scope in the true section (due to condition when true)
|
||||
if (o1 instanceof String s ? true : o2 instanceof String s) {} //error - the same binding variable in condition when true and false section when true
|
||||
if (!(o1 instanceof String s) ? o2 instanceof String s : true) {} //error - the same binding variable in condition when false and true section when true
|
||||
if (args.length == 1 ? o2 instanceof String s : o2 instanceof String s) {} //error - the same binding variable in true section when true and false section when true
|
||||
if (o1 instanceof String s ? true : !(o2 instanceof String s)) {} //error - the same binding variable in condition when true and false section when false
|
||||
if (!(o1 instanceof String s) ? !(o2 instanceof String s) : true) {} //error - the same binding variable in condition when false and true section when false
|
||||
if (args.length == 1 ? !(o2 instanceof String s) : !(o2 instanceof String s)) {} //error - the same binding variable in true section when false and false section when false
|
||||
|
||||
//verify that errors are reported to clashes in expression in non-conditional statements:
|
||||
boolean b = o1 instanceof String s && o2 instanceof String s;
|
||||
b = o1 instanceof String s && o2 instanceof String s;
|
||||
b &= o1 instanceof String s && o2 instanceof String s;
|
||||
assert o1 instanceof String s && o2 instanceof String s;
|
||||
testMethod(o1 instanceof String s && o2 instanceof String s, false);
|
||||
b = switch (args.length) { default -> o1 instanceof String s && o2 instanceof String s; };
|
||||
b = switch (args.length) { default -> { yield o1 instanceof String s && o2 instanceof String s; } };
|
||||
if (true) return o1 instanceof String s && o2 instanceof String s;
|
||||
|
||||
//verify the bindings don't go through method invocation parameters or outside a lambda:
|
||||
b = ((VoidPredicate) () -> o1 instanceof String s).get() && s.isEmpty();
|
||||
testMethod(o1 instanceof String s, s.isEmpty());
|
||||
}
|
||||
|
||||
static void testMethod(boolean b1, boolean b2) {}
|
||||
interface VoidPredicate {
|
||||
public boolean get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,27 @@
|
|||
DuplicateBindingTest.java:16:43: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[])
|
||||
DuplicateBindingTest.java:25:60: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:26:62: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:27:39: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:30:63: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:31:65: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:32:36: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:35:59: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:36:36: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:37:39: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:38:30: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:39:36: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:40:39: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:41:30: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:44:68: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:45:60: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:46:61: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:47:63: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:48:67: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:49:94: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:50:102: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:51:73: compiler.err.match.binding.exists
|
||||
DuplicateBindingTest.java:54:69: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, DuplicateBindingTest, null)
|
||||
DuplicateBindingTest.java:55:44: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, DuplicateBindingTest, null)
|
||||
- compiler.note.preview.filename: DuplicateBindingTest.java
|
||||
- compiler.note.preview.recompile
|
||||
1 error
|
||||
24 errors
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
MatchBindingScopeTest.java:19:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
|
||||
MatchBindingScopeTest.java:23:36: compiler.err.match.binding.exists
|
||||
MatchBindingScopeTest.java:23:61: compiler.err.match.binding.exists
|
||||
MatchBindingScopeTest.java:30:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
|
||||
MatchBindingScopeTest.java:31:32: compiler.err.cant.resolve.location: kindname.variable, k, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
|
||||
MatchBindingScopeTest.java:34:39: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue