mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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;
|
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;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.
|
/** A class for method symbols.
|
||||||
*/
|
*/
|
||||||
public static class MethodSymbol extends Symbol implements ExecutableElement {
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.ArgumentAttr.LocalCacheContext;
|
||||||
import com.sun.tools.javac.comp.Check.CheckContext;
|
import com.sun.tools.javac.comp.Check.CheckContext;
|
||||||
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
|
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 com.sun.tools.javac.jvm.*;
|
||||||
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
|
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
|
||||||
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
|
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
|
||||||
|
@ -642,6 +642,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
*/
|
*/
|
||||||
Type result;
|
Type result;
|
||||||
|
|
||||||
|
MatchBindings matchBindings = MatchBindingsComputer.EMPTY;
|
||||||
|
|
||||||
/** Visitor method: attribute a tree, catching any completion failure
|
/** Visitor method: attribute a tree, catching any completion failure
|
||||||
* exceptions. Return the tree's type.
|
* exceptions. Return the tree's type.
|
||||||
*
|
*
|
||||||
|
@ -660,6 +662,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
} else {
|
} else {
|
||||||
tree.accept(this);
|
tree.accept(this);
|
||||||
}
|
}
|
||||||
|
matchBindings = matchBindingsComputer.finishBindings(tree,
|
||||||
|
matchBindings);
|
||||||
if (tree == breakTree &&
|
if (tree == breakTree &&
|
||||||
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
|
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
|
||||||
breakTreeFound(copyEnv(env));
|
breakTreeFound(copyEnv(env));
|
||||||
|
@ -1418,18 +1422,18 @@ public class Attr extends JCTree.Visitor {
|
||||||
attribExpr(tree.cond, env, syms.booleanType);
|
attribExpr(tree.cond, env, syms.booleanType);
|
||||||
if (!breaksOutOf(tree, tree.body)) {
|
if (!breaksOutOf(tree, tree.body)) {
|
||||||
//include condition's body when false after the while, if cannot get out of the loop
|
//include condition's body when false after the while, if cannot get out of the loop
|
||||||
List<BindingSymbol> bindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
|
MatchBindings condBindings = matchBindings;
|
||||||
|
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||||
bindings.forEach(env.info.scope::enter);
|
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||||
bindings.forEach(BindingSymbol::preserveBinding);
|
|
||||||
}
|
}
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitWhileLoop(JCWhileLoop tree) {
|
public void visitWhileLoop(JCWhileLoop tree) {
|
||||||
attribExpr(tree.cond, env, syms.booleanType);
|
attribExpr(tree.cond, env, syms.booleanType);
|
||||||
|
MatchBindings condBindings = matchBindings;
|
||||||
// include condition's bindings when true in the body:
|
// 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 {
|
try {
|
||||||
attribStat(tree.body, whileEnv.dup(tree));
|
attribStat(tree.body, whileEnv.dup(tree));
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -1437,11 +1441,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
}
|
}
|
||||||
if (!breaksOutOf(tree, tree.body)) {
|
if (!breaksOutOf(tree, tree.body)) {
|
||||||
//include condition's bindings when false after the while, if cannot get out of the loop
|
//include condition's bindings when false after the while, if cannot get out of the loop
|
||||||
List<BindingSymbol> bindings =
|
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||||
matchBindingsComputer.getMatchBindings(tree.cond, false);
|
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||||
|
|
||||||
bindings.forEach(env.info.scope::enter);
|
|
||||||
bindings.forEach(BindingSymbol::preserveBinding);
|
|
||||||
}
|
}
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
|
@ -1454,15 +1455,15 @@ public class Attr extends JCTree.Visitor {
|
||||||
public void visitForLoop(JCForLoop tree) {
|
public void visitForLoop(JCForLoop tree) {
|
||||||
Env<AttrContext> loopEnv =
|
Env<AttrContext> loopEnv =
|
||||||
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
|
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
|
||||||
|
MatchBindings condBindings = MatchBindingsComputer.EMPTY;
|
||||||
try {
|
try {
|
||||||
attribStats(tree.init, loopEnv);
|
attribStats(tree.init, loopEnv);
|
||||||
List<BindingSymbol> matchBindings = List.nil();
|
|
||||||
if (tree.cond != null) {
|
if (tree.cond != null) {
|
||||||
attribExpr(tree.cond, loopEnv, syms.booleanType);
|
attribExpr(tree.cond, loopEnv, syms.booleanType);
|
||||||
// include condition's bindings when true in the body and step:
|
// 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 {
|
try {
|
||||||
bodyEnv.tree = tree; // before, we were not in loop!
|
bodyEnv.tree = tree; // before, we were not in loop!
|
||||||
attribStats(tree.step, bodyEnv);
|
attribStats(tree.step, bodyEnv);
|
||||||
|
@ -1477,11 +1478,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
}
|
}
|
||||||
if (!breaksOutOf(tree, tree.body)) {
|
if (!breaksOutOf(tree, tree.body)) {
|
||||||
//include condition's body when false after the while, if cannot get out of the loop
|
//include condition's body when false after the while, if cannot get out of the loop
|
||||||
List<BindingSymbol> bindings =
|
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
|
||||||
matchBindingsComputer.getMatchBindings(tree.cond, false);
|
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
|
||||||
|
|
||||||
bindings.forEach(env.info.scope::enter);
|
|
||||||
bindings.forEach(BindingSymbol::preserveBinding);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1818,6 +1816,7 @@ public class Attr extends JCTree.Visitor {
|
||||||
|
|
||||||
public void visitConditional(JCConditional tree) {
|
public void visitConditional(JCConditional tree) {
|
||||||
Type condtype = attribExpr(tree.cond, env, syms.booleanType);
|
Type condtype = attribExpr(tree.cond, env, syms.booleanType);
|
||||||
|
MatchBindings condBindings = matchBindings;
|
||||||
|
|
||||||
tree.polyKind = (!allowPoly ||
|
tree.polyKind = (!allowPoly ||
|
||||||
pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly ||
|
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
|
// include x's bindings when false in z
|
||||||
|
|
||||||
Type truetype;
|
Type truetype;
|
||||||
Env<AttrContext> trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
|
Env<AttrContext> trueEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
|
||||||
try {
|
try {
|
||||||
truetype = attribTree(tree.truepart, trueEnv, condInfo);
|
truetype = attribTree(tree.truepart, trueEnv, condInfo);
|
||||||
} finally {
|
} finally {
|
||||||
trueEnv.info.scope.leave();
|
trueEnv.info.scope.leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MatchBindings trueBindings = matchBindings;
|
||||||
|
|
||||||
Type falsetype;
|
Type falsetype;
|
||||||
Env<AttrContext> falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, false));
|
Env<AttrContext> falseEnv = bindingEnv(env, condBindings.bindingsWhenFalse);
|
||||||
try {
|
try {
|
||||||
falsetype = attribTree(tree.falsepart, falseEnv, condInfo);
|
falsetype = attribTree(tree.falsepart, falseEnv, condInfo);
|
||||||
} finally {
|
} finally {
|
||||||
falseEnv.info.scope.leave();
|
falseEnv.info.scope.leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MatchBindings falseBindings = matchBindings;
|
||||||
|
|
||||||
Type owntype = (tree.polyKind == PolyKind.STANDALONE) ?
|
Type owntype = (tree.polyKind == PolyKind.STANDALONE) ?
|
||||||
condType(List.of(tree.truepart.pos(), tree.falsepart.pos()),
|
condType(List.of(tree.truepart.pos(), tree.falsepart.pos()),
|
||||||
List.of(truetype, falsetype)) : pt();
|
List.of(truetype, falsetype)) : pt();
|
||||||
|
@ -1867,6 +1870,7 @@ public class Attr extends JCTree.Visitor {
|
||||||
owntype = cfolder.coerce(condtype.isTrue() ? truetype : falsetype, owntype);
|
owntype = cfolder.coerce(condtype.isTrue() ? truetype : falsetype, owntype);
|
||||||
}
|
}
|
||||||
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
||||||
|
matchBindings = matchBindingsComputer.conditional(tree, condBindings, trueBindings, falseBindings);
|
||||||
}
|
}
|
||||||
//where
|
//where
|
||||||
private boolean isBooleanOrNumeric(Env<AttrContext> env, JCExpression tree) {
|
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 true in y
|
||||||
// include x's bindings when false in z
|
// include x's bindings when false in z
|
||||||
|
|
||||||
List<BindingSymbol> thenBindings = matchBindingsComputer.getMatchBindings(tree.cond, true);
|
MatchBindings condBindings = matchBindings;
|
||||||
Env<AttrContext> thenEnv = bindingEnv(env, thenBindings);
|
Env<AttrContext> thenEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
attribStat(tree.thenpart, thenEnv);
|
attribStat(tree.thenpart, thenEnv);
|
||||||
|
@ -2034,10 +2038,9 @@ public class Attr extends JCTree.Visitor {
|
||||||
preFlow(tree.thenpart);
|
preFlow(tree.thenpart);
|
||||||
boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make);
|
boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make);
|
||||||
boolean aliveAfterElse;
|
boolean aliveAfterElse;
|
||||||
List<BindingSymbol> elseBindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
|
|
||||||
|
|
||||||
if (tree.elsepart != null) {
|
if (tree.elsepart != null) {
|
||||||
Env<AttrContext> elseEnv = bindingEnv(env, elseBindings);
|
Env<AttrContext> elseEnv = bindingEnv(env, condBindings.bindingsWhenFalse);
|
||||||
try {
|
try {
|
||||||
attribStat(tree.elsepart, elseEnv);
|
attribStat(tree.elsepart, elseEnv);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -2054,9 +2057,9 @@ public class Attr extends JCTree.Visitor {
|
||||||
List<BindingSymbol> afterIfBindings = List.nil();
|
List<BindingSymbol> afterIfBindings = List.nil();
|
||||||
|
|
||||||
if (aliveAfterThen && !aliveAfterElse) {
|
if (aliveAfterThen && !aliveAfterElse) {
|
||||||
afterIfBindings = thenBindings;
|
afterIfBindings = condBindings.bindingsWhenTrue;
|
||||||
} else if (aliveAfterElse && !aliveAfterThen) {
|
} else if (aliveAfterElse && !aliveAfterThen) {
|
||||||
afterIfBindings = elseBindings;
|
afterIfBindings = condBindings.bindingsWhenFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
afterIfBindings.forEach(env.info.scope::enter);
|
afterIfBindings.forEach(env.info.scope::enter);
|
||||||
|
@ -3767,6 +3770,7 @@ public class Attr extends JCTree.Visitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
result = check(tree, owntype, KindSelector.VAL, resultInfo);
|
||||||
|
matchBindings = matchBindingsComputer.unary(tree, matchBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitBinary(JCBinary tree) {
|
public void visitBinary(JCBinary tree) {
|
||||||
|
@ -3778,19 +3782,20 @@ public class Attr extends JCTree.Visitor {
|
||||||
// x || y
|
// x || y
|
||||||
// include x's bindings when false in y
|
// include x's bindings when false in y
|
||||||
|
|
||||||
List<BindingSymbol> matchBindings;
|
MatchBindings lhsBindings = matchBindings;
|
||||||
|
List<BindingSymbol> propagatedBindings;
|
||||||
switch (tree.getTag()) {
|
switch (tree.getTag()) {
|
||||||
case AND:
|
case AND:
|
||||||
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
|
propagatedBindings = lhsBindings.bindingsWhenTrue;
|
||||||
break;
|
break;
|
||||||
case OR:
|
case OR:
|
||||||
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false);
|
propagatedBindings = lhsBindings.bindingsWhenFalse;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
matchBindings = List.nil();
|
propagatedBindings = List.nil();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Env<AttrContext> rhsEnv = bindingEnv(env, matchBindings);
|
Env<AttrContext> rhsEnv = bindingEnv(env, propagatedBindings);
|
||||||
Type right;
|
Type right;
|
||||||
try {
|
try {
|
||||||
right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv));
|
right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv));
|
||||||
|
@ -3798,6 +3803,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
rhsEnv.info.scope.leave();
|
rhsEnv.info.scope.leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matchBindings = matchBindingsComputer.binary(tree, lhsBindings, matchBindings);
|
||||||
|
|
||||||
// Find operator.
|
// Find operator.
|
||||||
Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right);
|
Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right);
|
||||||
Type owntype = types.createErrorType(tree.type);
|
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.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos());
|
||||||
annotate.flush();
|
annotate.flush();
|
||||||
result = tree.type;
|
result = tree.type;
|
||||||
|
matchBindings = new MatchBindings(List.of(tree.symbol), List.nil());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitIndexed(JCArrayAccess tree) {
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 &&
|
} else if ((sym.flags() & MATCH_BINDING) != 0 &&
|
||||||
(byName.flags() & MATCH_BINDING) != 0 &&
|
(byName.flags() & MATCH_BINDING) != 0 &&
|
||||||
(byName.flags() & MATCH_BINDING_TO_OUTER) == 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;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
duplicateError(pos, byName);
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,31 +25,26 @@
|
||||||
|
|
||||||
package com.sun.tools.javac.comp;
|
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;
|
||||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
import com.sun.tools.javac.code.Symbol.BindingSymbol;
|
||||||
import com.sun.tools.javac.code.Type;
|
|
||||||
import com.sun.tools.javac.code.Types;
|
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBinary;
|
import com.sun.tools.javac.tree.JCTree.Tag;
|
||||||
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.TreeScanner;
|
import com.sun.tools.javac.tree.TreeScanner;
|
||||||
import com.sun.tools.javac.util.Context;
|
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.List;
|
||||||
import com.sun.tools.javac.util.Log;
|
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 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<>();
|
protected static final Context.Key<MatchBindingsComputer> matchBindingsComputerKey = new Context.Key<>();
|
||||||
|
|
||||||
private final Log log;
|
private final Log log;
|
||||||
private final Types types;
|
|
||||||
boolean whenTrue;
|
|
||||||
List<BindingSymbol> bindings;
|
|
||||||
|
|
||||||
public static MatchBindingsComputer instance(Context context) {
|
public static MatchBindingsComputer instance(Context context) {
|
||||||
MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
|
MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
|
||||||
|
@ -60,106 +55,99 @@ public class MatchBindingsComputer extends TreeScanner {
|
||||||
|
|
||||||
protected MatchBindingsComputer(Context context) {
|
protected MatchBindingsComputer(Context context) {
|
||||||
this.log = Log.instance(context);
|
this.log = Log.instance(context);
|
||||||
this.types = Types.instance(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BindingSymbol> getMatchBindings(JCTree expression, boolean whenTrue) {
|
public MatchBindings conditional(JCTree tree, MatchBindings condBindings, MatchBindings trueBindings, MatchBindings falseBindings) {
|
||||||
this.whenTrue = whenTrue;
|
if (condBindings == EMPTY &&
|
||||||
this.bindings = List.nil();
|
trueBindings == EMPTY &&
|
||||||
scan(expression);
|
falseBindings == EMPTY) {
|
||||||
return bindings;
|
return EMPTY;
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
DiagnosticPosition pos = tree.pos();
|
||||||
public void visitUnary(JCUnary tree) {
|
//A pattern variable is introduced both by a when true, and by c when true:
|
||||||
switch (tree.getTag()) {
|
List<BindingSymbol> xTzT = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
|
||||||
case NOT:
|
//A pattern variable is introduced both by a when false, and by b when true:
|
||||||
// e.T = x.F // flip 'em
|
List<BindingSymbol> xFyT = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenTrue);
|
||||||
// e.F = x.T
|
//A pattern variable is introduced both by b when true, and by c when true:
|
||||||
whenTrue = !whenTrue;
|
List<BindingSymbol> yTzT = intersection(pos, trueBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
|
||||||
scan(tree.arg);
|
//A pattern variable is introduced both by a when true, and by c when false:
|
||||||
whenTrue = !whenTrue;
|
List<BindingSymbol> xTzF = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenFalse);
|
||||||
break;
|
//A pattern variable is introduced both by a when false, and by b when false:
|
||||||
default:
|
List<BindingSymbol> xFyF = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenFalse);
|
||||||
super.visitUnary(tree);
|
//A pattern variable is introduced both by b when false, and by c when false:
|
||||||
break;
|
List<BindingSymbol> yFzF = intersection(pos, trueBindings.bindingsWhenFalse, falseBindings.bindingsWhenFalse);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
//error recovery:
|
||||||
public void visitConditional(JCConditional tree) {
|
|
||||||
/* if e = "x ? y : z", then:
|
/* 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.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))
|
e.F = union(intersect(y.F, z.F), intersect(x.T, z.F), intersect(x.F, y.F))
|
||||||
*/
|
*/
|
||||||
if (whenTrue) {
|
List<BindingSymbol> bindingsWhenTrue = union(pos, yTzT, xTzT, xFyT);
|
||||||
List<BindingSymbol> xT, yT, zT, xF;
|
List<BindingSymbol> bindingsWhenFalse = union(pos, yFzF, xTzF, xFyF);
|
||||||
scan(tree.cond);
|
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
|
||||||
xT = bindings;
|
}
|
||||||
scan(tree.truepart);
|
|
||||||
yT = bindings;
|
public MatchBindings unary(JCTree tree, MatchBindings bindings) {
|
||||||
scan(tree.falsepart);
|
if (bindings == EMPTY || !tree.hasTag(Tag.NOT)) return bindings;
|
||||||
zT = bindings;
|
return new MatchBindings(bindings.bindingsWhenFalse, bindings.bindingsWhenTrue);
|
||||||
whenTrue = false;
|
}
|
||||||
scan(tree.cond);
|
|
||||||
xF = bindings;
|
public MatchBindings binary(JCTree tree, MatchBindings lhsBindings, MatchBindings rhsBindings) {
|
||||||
whenTrue = true;
|
switch (tree.getTag()) {
|
||||||
bindings = union(tree, intersection(tree, yT, zT), intersection(tree, xT, zT), intersection(tree, xF, yT));
|
case AND: {
|
||||||
} else {
|
// e.T = union(x.T, y.T)
|
||||||
List<BindingSymbol> xF, yF, zF, xT;
|
// e.F = intersection(x.F, y.F) (error recovery)
|
||||||
scan(tree.cond);
|
List<BindingSymbol> bindingsWhenTrue =
|
||||||
xF = bindings;
|
union(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
|
||||||
scan(tree.truepart);
|
List<BindingSymbol> bindingsWhenFalse = //error recovery
|
||||||
yF = bindings;
|
intersection(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
|
||||||
scan(tree.falsepart);
|
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
|
||||||
zF = bindings;
|
}
|
||||||
whenTrue = true;
|
case OR: {
|
||||||
scan(tree.cond);
|
// e.T = intersection(x.T, y.T) (error recovery)
|
||||||
xT = bindings;
|
// e.F = union(x.F, y.F)
|
||||||
whenTrue = false;
|
List<BindingSymbol> bindingsWhenTrue = //error recovery
|
||||||
bindings = union(tree, intersection(tree, yF, zF), intersection(tree, xT, zF), intersection(tree, xF, yF));
|
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)
|
// 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();
|
List<BindingSymbol> list = List.nil();
|
||||||
for (BindingSymbol v1 : lhsBindings) {
|
for (BindingSymbol v1 : lhsBindings) {
|
||||||
for (BindingSymbol v2 : rhsBindings) {
|
for (BindingSymbol v2 : rhsBindings) {
|
||||||
if (v1.name == v2.name) {
|
if (v1.name == v2.name &&
|
||||||
log.error(tree.pos(), Errors.MatchBindingExists);
|
(v1.flags() & CLASH) == 0 &&
|
||||||
|
(v2.flags() & CLASH) == 0) {
|
||||||
|
log.error(pos, Errors.MatchBindingExists);
|
||||||
list = list.append(v2);
|
list = list.append(v2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,14 +156,16 @@ public class MatchBindingsComputer extends TreeScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@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).
|
// It is an error if for union(a,b), a and b contain the same name (disjoint union).
|
||||||
List<BindingSymbol> list = lhsBindings;
|
List<BindingSymbol> list = lhsBindings;
|
||||||
for (List<BindingSymbol> rhsBindings : rhsBindings_s) {
|
for (List<BindingSymbol> rhsBindings : rhsBindings_s) {
|
||||||
for (BindingSymbol v : rhsBindings) {
|
for (BindingSymbol v : rhsBindings) {
|
||||||
for (BindingSymbol ov : list) {
|
for (BindingSymbol ov : list) {
|
||||||
if (ov.name == v.name) {
|
if (ov.name == v.name &&
|
||||||
log.error(tree.pos(), Errors.MatchBindingExists);
|
(ov.flags() & CLASH) == 0 &&
|
||||||
|
(v.flags() & CLASH) == 0) {
|
||||||
|
log.error(pos, Errors.MatchBindingExists);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list = list.append(v);
|
list = list.append(v);
|
||||||
|
@ -183,34 +173,4 @@ public class MatchBindingsComputer extends TreeScanner {
|
||||||
}
|
}
|
||||||
return list;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,11 +26,12 @@
|
||||||
package com.sun.tools.javac.comp;
|
package com.sun.tools.javac.comp;
|
||||||
|
|
||||||
import com.sun.tools.javac.code.Flags;
|
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.Symbol.VarSymbol;
|
||||||
import com.sun.tools.javac.code.Symtab;
|
import com.sun.tools.javac.code.Symtab;
|
||||||
import com.sun.tools.javac.code.Type;
|
import com.sun.tools.javac.code.Type;
|
||||||
import com.sun.tools.javac.code.Types;
|
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;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCAssign;
|
import com.sun.tools.javac.tree.JCTree.JCAssign;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBinary;
|
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.JCInstanceOf;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
|
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
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.JCVariableDecl;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
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.tree.TreeTranslator;
|
||||||
import com.sun.tools.javac.util.Assert;
|
import com.sun.tools.javac.util.Assert;
|
||||||
import com.sun.tools.javac.util.Context;
|
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.ListBuffer;
|
||||||
import com.sun.tools.javac.util.Log;
|
import com.sun.tools.javac.util.Log;
|
||||||
import com.sun.tools.javac.util.Names;
|
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;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
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 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.jvm.Target;
|
||||||
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBlock;
|
import com.sun.tools.javac.tree.JCTree.JCBlock;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
|
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.JCStatement;
|
||||||
import com.sun.tools.javac.tree.JCTree.LetExpr;
|
import com.sun.tools.javac.tree.JCTree.LetExpr;
|
||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This pass translates pattern-matching constructs, such as instanceof <pattern>.
|
* 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 ConstFold constFold;
|
||||||
private final Names names;
|
private final Names names;
|
||||||
private final Target target;
|
private final Target target;
|
||||||
private final MatchBindingsComputer matchBindingsComputer;
|
|
||||||
private TreeMaker make;
|
private TreeMaker make;
|
||||||
|
|
||||||
BindingContext bindingContext = new BindingContext() {
|
BindingContext bindingContext = new BindingContext() {
|
||||||
|
@Override
|
||||||
|
VarSymbol bindingDeclared(BindingSymbol varSymbol) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
VarSymbol getBindingFor(BindingSymbol varSymbol) {
|
VarSymbol getBindingFor(BindingSymbol varSymbol) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -140,7 +143,6 @@ public class TransPatterns extends TreeTranslator {
|
||||||
constFold = ConstFold.instance(context);
|
constFold = ConstFold.instance(context);
|
||||||
names = Names.instance(context);
|
names = Names.instance(context);
|
||||||
target = Target.instance(context);
|
target = Target.instance(context);
|
||||||
matchBindingsComputer = MatchBindingsComputer.instance(context);
|
|
||||||
debugTransPatterns = Options.instance(context).isSet("debug.patterns");
|
debugTransPatterns = Options.instance(context).isSet("debug.patterns");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,8 +166,8 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
|
result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
|
||||||
|
|
||||||
VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol);
|
VarSymbol bindingVar = bindingContext.bindingDeclared(patt.symbol);
|
||||||
if (bindingVar != null) {
|
if (bindingVar != null) { //TODO: cannot be null here?
|
||||||
JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
|
JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
|
||||||
make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
|
make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
|
||||||
result = makeBinary(Tag.AND, (JCExpression)result,
|
result = makeBinary(Tag.AND, (JCExpression)result,
|
||||||
|
@ -180,20 +182,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBinary(JCBinary tree) {
|
public void visitBinary(JCBinary tree) {
|
||||||
List<BindingSymbol> matchBindings;
|
bindingContext = new BasicBindingContext();
|
||||||
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);
|
|
||||||
try {
|
try {
|
||||||
super.visitBinary(tree);
|
super.visitBinary(tree);
|
||||||
result = bindingContext.decorateExpression(tree);
|
result = bindingContext.decorateExpression(tree);
|
||||||
|
@ -204,9 +193,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitConditional(JCConditional tree) {
|
public void visitConditional(JCConditional tree) {
|
||||||
bindingContext = new BasicBindingContext(
|
bindingContext = new BasicBindingContext();
|
||||||
matchBindingsComputer.getMatchBindings(tree.cond, true)
|
|
||||||
.appendList(matchBindingsComputer.getMatchBindings(tree.cond, false)));
|
|
||||||
try {
|
try {
|
||||||
super.visitConditional(tree);
|
super.visitConditional(tree);
|
||||||
result = bindingContext.decorateExpression(tree);
|
result = bindingContext.decorateExpression(tree);
|
||||||
|
@ -217,7 +204,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitIf(JCIf tree) {
|
public void visitIf(JCIf tree) {
|
||||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
bindingContext = new BasicBindingContext();
|
||||||
try {
|
try {
|
||||||
super.visitIf(tree);
|
super.visitIf(tree);
|
||||||
result = bindingContext.decorateStatement(tree);
|
result = bindingContext.decorateStatement(tree);
|
||||||
|
@ -228,7 +215,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitForLoop(JCForLoop tree) {
|
public void visitForLoop(JCForLoop tree) {
|
||||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
bindingContext = new BasicBindingContext();
|
||||||
try {
|
try {
|
||||||
super.visitForLoop(tree);
|
super.visitForLoop(tree);
|
||||||
result = bindingContext.decorateStatement(tree);
|
result = bindingContext.decorateStatement(tree);
|
||||||
|
@ -239,7 +226,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitWhileLoop(JCWhileLoop tree) {
|
public void visitWhileLoop(JCWhileLoop tree) {
|
||||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
bindingContext = new BasicBindingContext();
|
||||||
try {
|
try {
|
||||||
super.visitWhileLoop(tree);
|
super.visitWhileLoop(tree);
|
||||||
result = bindingContext.decorateStatement(tree);
|
result = bindingContext.decorateStatement(tree);
|
||||||
|
@ -250,7 +237,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitDoLoop(JCDoWhileLoop tree) {
|
public void visitDoLoop(JCDoWhileLoop tree) {
|
||||||
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
|
bindingContext = new BasicBindingContext();
|
||||||
try {
|
try {
|
||||||
super.visitDoLoop(tree);
|
super.visitDoLoop(tree);
|
||||||
result = bindingContext.decorateStatement(tree);
|
result = bindingContext.decorateStatement(tree);
|
||||||
|
@ -286,7 +273,7 @@ public class TransPatterns extends TreeTranslator {
|
||||||
@Override
|
@Override
|
||||||
public void visitBlock(JCBlock tree) {
|
public void visitBlock(JCBlock tree) {
|
||||||
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
||||||
bindingContext = new BasicBindingContext(List.nil()) {
|
bindingContext = new BindingDeclarationFenceBindingContext() {
|
||||||
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
|
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
|
||||||
//{
|
//{
|
||||||
// if (E instanceof T N) {
|
// 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) {
|
public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
|
||||||
try {
|
try {
|
||||||
this.make = make;
|
this.make = make;
|
||||||
|
@ -360,11 +358,8 @@ public class TransPatterns extends TreeTranslator {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BindingSymbol> getMatchBindings(JCExpression cond) {
|
|
||||||
return matchBindingsComputer.getMatchBindings(cond, true)
|
|
||||||
.appendList(matchBindingsComputer.getMatchBindings(cond, false));
|
|
||||||
}
|
|
||||||
abstract class BindingContext {
|
abstract class BindingContext {
|
||||||
|
abstract VarSymbol bindingDeclared(BindingSymbol varSymbol);
|
||||||
abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
|
abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
|
||||||
abstract JCStatement decorateStatement(JCStatement stat);
|
abstract JCStatement decorateStatement(JCStatement stat);
|
||||||
abstract JCExpression decorateExpression(JCExpression expr);
|
abstract JCExpression decorateExpression(JCExpression expr);
|
||||||
|
@ -373,20 +368,23 @@ public class TransPatterns extends TreeTranslator {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BasicBindingContext extends BindingContext {
|
class BasicBindingContext extends BindingContext {
|
||||||
List<BindingSymbol> matchBindings;
|
|
||||||
Map<BindingSymbol, VarSymbol> hoistedVarMap;
|
Map<BindingSymbol, VarSymbol> hoistedVarMap;
|
||||||
BindingContext parent;
|
BindingContext parent;
|
||||||
|
|
||||||
public BasicBindingContext(List<BindingSymbol> matchBindings) {
|
public BasicBindingContext() {
|
||||||
this.matchBindings = matchBindings;
|
|
||||||
this.parent = bindingContext;
|
this.parent = bindingContext;
|
||||||
this.hoistedVarMap = matchBindings.stream()
|
this.hoistedVarMap = new HashMap<>();
|
||||||
.filter(v -> parent.getBindingFor(v) == null)
|
}
|
||||||
.collect(Collectors.toMap(v -> v, v -> {
|
|
||||||
VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner);
|
@Override
|
||||||
res.setTypeAttributes(v.getRawTypeAttributes());
|
VarSymbol bindingDeclared(BindingSymbol varSymbol) {
|
||||||
return res;
|
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
|
@Override
|
||||||
|
@ -454,4 +452,13 @@ public class TransPatterns extends TreeTranslator {
|
||||||
return make.at(pos).VarDef(varSymbol, null);
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.Directive.RequiresDirective;
|
||||||
import com.sun.tools.javac.code.Scope.*;
|
import com.sun.tools.javac.code.Scope.*;
|
||||||
import com.sun.tools.javac.code.Symbol.*;
|
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.*;
|
||||||
import com.sun.tools.javac.util.DefinedBy.Api;
|
import com.sun.tools.javac.util.DefinedBy.Api;
|
||||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
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: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: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)
|
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 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; }));
|
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");
|
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;
|
int f;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static boolean main(String[] args) {
|
||||||
|
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
int s;
|
int s;
|
||||||
|
@ -18,5 +18,45 @@ public class DuplicateBindingTest {
|
||||||
if (args[0] instanceof String f) { // OK to redef field.
|
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: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.filename: DuplicateBindingTest.java
|
||||||
- compiler.note.preview.recompile
|
- 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: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: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: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)
|
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