8236670: Conflicting bindings accepted in some cases

Reviewed-by: mcimadamore
This commit is contained in:
Jan Lahoda 2020-01-14 11:10:07 +01:00
parent eccf39b295
commit a35d087f54
13 changed files with 460 additions and 222 deletions

View file

@ -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;

View file

@ -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 {

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}
@Override
public void visitBindingPattern(JCBindingPattern tree) {
bindings = whenTrue ? List.of(tree.symbol) : List.nil();
}
@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;
public MatchBindings conditional(JCTree tree, MatchBindings condBindings, MatchBindings trueBindings, MatchBindings falseBindings) {
if (condBindings == EMPTY &&
trueBindings == EMPTY &&
falseBindings == EMPTY) {
return EMPTY;
}
}
@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;
}
}
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 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;
}
}
}

View file

@ -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());
return res;
}));
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;
}
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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;
}
}

View 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;
}
}
}

View file

@ -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();
}
}

View file

@ -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

View file

@ -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)