8254105: allow static nested declarations

Reviewed-by: mcimadamore
This commit is contained in:
Vicente Romero 2020-11-21 03:17:57 +00:00
parent 14de791d60
commit 9a19eb6918
25 changed files with 581 additions and 459 deletions

View File

@ -383,30 +383,31 @@ public class Flags {
/** Modifier masks. /** Modifier masks.
*/ */
public static final int public static final int
AccessFlags = PUBLIC | PROTECTED | PRIVATE, AccessFlags = PUBLIC | PROTECTED | PRIVATE,
LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC, LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC,
StaticLocalFlags = LocalClassFlags | STATIC | INTERFACE, StaticLocalFlags = LocalClassFlags | STATIC | INTERFACE,
MemberClassFlags = LocalClassFlags | INTERFACE | AccessFlags, MemberClassFlags = LocalClassFlags | INTERFACE | AccessFlags,
MemberRecordFlags = MemberClassFlags | STATIC, MemberStaticClassFlags = MemberClassFlags | STATIC,
ClassFlags = LocalClassFlags | INTERFACE | PUBLIC | ANNOTATION, ClassFlags = LocalClassFlags | INTERFACE | PUBLIC | ANNOTATION,
InterfaceVarFlags = FINAL | STATIC | PUBLIC, InterfaceVarFlags = FINAL | STATIC | PUBLIC,
VarFlags = AccessFlags | FINAL | STATIC | VarFlags = AccessFlags | FINAL | STATIC |
VOLATILE | TRANSIENT | ENUM, VOLATILE | TRANSIENT | ENUM,
ConstructorFlags = AccessFlags, ConstructorFlags = AccessFlags,
InterfaceMethodFlags = ABSTRACT | PUBLIC, InterfaceMethodFlags = ABSTRACT | PUBLIC,
MethodFlags = AccessFlags | ABSTRACT | STATIC | NATIVE | MethodFlags = AccessFlags | ABSTRACT | STATIC | NATIVE |
SYNCHRONIZED | FINAL | STRICTFP, SYNCHRONIZED | FINAL | STRICTFP,
RecordMethodFlags = AccessFlags | ABSTRACT | STATIC | RecordMethodFlags = AccessFlags | ABSTRACT | STATIC |
SYNCHRONIZED | FINAL | STRICTFP; SYNCHRONIZED | FINAL | STRICTFP;
public static final long public static final long
ExtendedStandardFlags = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED, ExtendedStandardFlags = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED,
ExtendedMemberClassFlags = (long)MemberClassFlags | SEALED | NON_SEALED, ExtendedMemberClassFlags = (long)MemberClassFlags | SEALED | NON_SEALED,
ExtendedClassFlags = (long)ClassFlags | SEALED | NON_SEALED, ExtendedMemberStaticClassFlags = (long) MemberStaticClassFlags | SEALED | NON_SEALED,
ModifierFlags = ((long)StandardFlags & ~INTERFACE) | DEFAULT | SEALED | NON_SEALED, ExtendedClassFlags = (long)ClassFlags | SEALED | NON_SEALED,
InterfaceMethodMask = ABSTRACT | PRIVATE | STATIC | PUBLIC | STRICTFP | DEFAULT, ModifierFlags = ((long)StandardFlags & ~INTERFACE) | DEFAULT | SEALED | NON_SEALED,
AnnotationTypeElementMask = ABSTRACT | PUBLIC, InterfaceMethodMask = ABSTRACT | PRIVATE | STATIC | PUBLIC | STRICTFP | DEFAULT,
LocalVarFlags = FINAL | PARAMETER, AnnotationTypeElementMask = ABSTRACT | PUBLIC,
ReceiverParamFlags = PARAMETER; LocalVarFlags = FINAL | PARAMETER,
ReceiverParamFlags = PARAMETER;
@SuppressWarnings("preview") @SuppressWarnings("preview")
public static Set<Modifier> asModifierSet(long flags) { public static Set<Modifier> asModifierSet(long flags) {

View File

@ -171,6 +171,7 @@ public class Attr extends JCTree.Visitor {
allowReifiableTypesInInstanceof = allowReifiableTypesInInstanceof =
Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) && Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) &&
(!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled()); (!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled());
allowRecords = Feature.RECORDS.allowedInSource(source);
sourceName = source.name; sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@ -207,6 +208,10 @@ public class Attr extends JCTree.Visitor {
*/ */
boolean allowReifiableTypesInInstanceof; boolean allowReifiableTypesInInstanceof;
/** Are records allowed
*/
private final boolean allowRecords;
/** /**
* Switch: warn about use of variable before declaration? * Switch: warn about use of variable before declaration?
* RFE: 6425594 * RFE: 6425594
@ -5309,14 +5314,15 @@ public class Attr extends JCTree.Visitor {
attribStat(l.head, env); attribStat(l.head, env);
// Check that declarations in inner classes are not static (JLS 8.1.2) // Check that declarations in inner classes are not static (JLS 8.1.2)
// Make an exception for static constants. // Make an exception for static constants.
if (c.owner.kind != PCK && if (!allowRecords &&
((c.flags() & STATIC) == 0 || c.name == names.empty) && c.owner.kind != PCK &&
(TreeInfo.flags(l.head) & (STATIC | INTERFACE)) != 0) { ((c.flags() & STATIC) == 0 || c.name == names.empty) &&
(TreeInfo.flags(l.head) & (STATIC | INTERFACE)) != 0) {
Symbol sym = null; Symbol sym = null;
if (l.head.hasTag(VARDEF)) sym = ((JCVariableDecl) l.head).sym; if (l.head.hasTag(VARDEF)) sym = ((JCVariableDecl) l.head).sym;
if (sym == null || if (sym == null ||
sym.kind != VAR || sym.kind != VAR ||
((VarSymbol) sym).getConstValue() == null) ((VarSymbol) sym).getConstValue() == null)
log.error(l.head.pos(), Errors.IclsCantHaveStaticDecl(c)); log.error(l.head.pos(), Errors.IclsCantHaveStaticDecl(c));
} }
} }

View File

@ -1216,23 +1216,20 @@ public class Check {
implicit |= sym.owner.flags_field & STRICTFP; implicit |= sym.owner.flags_field & STRICTFP;
break; break;
case TYP: case TYP:
if (sym.isLocal()) { if (sym.owner.kind.matches(KindSelector.VAL_MTH)) {
boolean implicitlyStatic = !sym.isAnonymous() && boolean implicitlyStatic = !sym.isAnonymous() &&
((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0); ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic; boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
// local statics are allowed only if records are allowed too
mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags; mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
implicit = implicitlyStatic ? STATIC : implicit; implicit = implicitlyStatic ? STATIC : implicit;
if (staticOrImplicitlyStatic) {
if (sym.owner.kind == TYP) {
log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
}
}
} else if (sym.owner.kind == TYP) { } else if (sym.owner.kind == TYP) {
mask = (flags & RECORD) != 0 ? MemberRecordFlags : ExtendedMemberClassFlags; // statics in inner classes are allowed only if records are allowed too
mask = ((flags & STATIC) != 0) && allowRecords ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
if (sym.owner.owner.kind == PCK || if (sym.owner.owner.kind == PCK ||
(sym.owner.flags_field & STATIC) != 0) (sym.owner.flags_field & STATIC) != 0) {
mask |= STATIC; mask |= STATIC;
else if ((flags & ENUM) != 0 || (flags & RECORD) != 0) { } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses); log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
} }
// Nested interfaces and enums are always STATIC (Spec ???) // Nested interfaces and enums are always STATIC (Spec ???)

View File

@ -24,10 +24,11 @@
/* /*
* @test * @test
* @bug 4279339 * @bug 4279339
* @summary Verify that an anonymous class cannot contain a static field. * @summary Verify that an anonymous class can contain a static field only if source >= 16
* @author maddox * @author maddox
* *
* @run compile/fail AnonStaticMember_1.java * @compile/fail/ref=AnonStaticMember_1.out -source 15 -XDrawDiagnostics AnonStaticMember_1.java
* @compile AnonStaticMember_1.java
*/ */
class AnonStaticMember_1 { class AnonStaticMember_1 {

View File

@ -0,0 +1,4 @@
- compiler.warn.source.no.system.modules.path: 15
AnonStaticMember_1.java:36:20: compiler.err.icls.cant.have.static.decl: compiler.misc.anonymous.class: AnonStaticMember_1$1
1 error
1 warning

View File

@ -1,10 +1,11 @@
/* /*
* @test /nodynamiccopyright/ * @test /nodynamiccopyright/
* @bug 4279339 6969184 * @bug 4279339 6969184
* @summary Verify that an anonymous class cannot contain a static method. * @summary Verify that an anonymous class can contain a static method only if source >= 16
* @author maddox * @author maddox
* *
* @run compile/fail/ref=AnonStaticMember_2.out -XDrawDiagnostics AnonStaticMember_2.java * @compile/fail/ref=AnonStaticMember_2.out -source 15 -XDrawDiagnostics AnonStaticMember_2.java
* @compile AnonStaticMember_2.java
*/ */
class AnonStaticMember_2 { class AnonStaticMember_2 {

View File

@ -1,2 +1,4 @@
AnonStaticMember_2.java:12:21: compiler.err.icls.cant.have.static.decl: compiler.misc.anonymous.class: AnonStaticMember_2$1 - compiler.warn.source.no.system.modules.path: 15
AnonStaticMember_2.java:13:21: compiler.err.icls.cant.have.static.decl: compiler.misc.anonymous.class: AnonStaticMember_2$1
1 error 1 error
1 warning

View File

@ -4,7 +4,8 @@
* @summary Verify rejection of illegal static variables in inner classes. * @summary Verify rejection of illegal static variables in inner classes.
* @author William Maddox (maddox) * @author William Maddox (maddox)
* *
* @compile/fail/ref=InnerNamedConstant_2.out -XDrawDiagnostics InnerNamedConstant_2.java * @compile/fail/ref=InnerNamedConstant_2_A.out -XDrawDiagnostics -source 15 InnerNamedConstant_2.java
* @compile/fail/ref=InnerNamedConstant_2_B.out -XDrawDiagnostics InnerNamedConstant_2.java
*/ */
public class InnerNamedConstant_2 { public class InnerNamedConstant_2 {

View File

@ -1,5 +0,0 @@
InnerNamedConstant_2.java:22:20: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner2
InnerNamedConstant_2.java:23:29: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner2
InnerNamedConstant_2.java:25:13: compiler.err.cant.assign.val.to.final.var: z
InnerNamedConstant_2.java:34:26: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner3
4 errors

View File

@ -0,0 +1,7 @@
- compiler.warn.source.no.system.modules.path: 15
InnerNamedConstant_2.java:23:20: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner2
InnerNamedConstant_2.java:24:29: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner2
InnerNamedConstant_2.java:26:13: compiler.err.cant.assign.val.to.final.var: z
InnerNamedConstant_2.java:35:26: compiler.err.icls.cant.have.static.decl: InnerNamedConstant_2.Inner3
4 errors
1 warning

View File

@ -0,0 +1,2 @@
InnerNamedConstant_2.java:26:13: compiler.err.cant.assign.val.to.final.var: z
1 error

View File

@ -1,10 +1,11 @@
/* /*
* @test /nodynamiccopyright/ * @test /nodynamiccopyright/
* @bug 4063740 6969184 * @bug 4063740 6969184
* @summary Interfaces may only be declared in top level classes. * @summary Interfaces can be declared in inner classes only for source >= 16
* @author turnidge * @author turnidge
* *
* @compile/fail/ref=InterfaceInInner.out -XDrawDiagnostics InterfaceInInner.java * @compile/fail/ref=InterfaceInInner.out -XDrawDiagnostics -source 15 InterfaceInInner.java
* @compile InterfaceInInner.java
*/ */
class InterfaceInInner { class InterfaceInInner {
InterfaceInInner() { InterfaceInInner() {

View File

@ -1,2 +1,4 @@
InterfaceInInner.java:12:13: compiler.err.static.declaration.not.allowed.in.inner.classes - compiler.warn.source.no.system.modules.path: 15
InterfaceInInner.java:13:13: compiler.err.icls.cant.have.static.decl: foo
1 error 1 error
1 warning

View File

@ -25,7 +25,8 @@
* @test * @test
* @bug 8222035 * @bug 8222035
* @summary minimal inference context optimization is forcing resolution with incomplete constraints * @summary minimal inference context optimization is forcing resolution with incomplete constraints
* @compile/fail/ref=MinContextOpTest.out -XDrawDiagnostics MinContextOpTest.java * @compile/fail/ref=MinContextOpTest_A.out -XDrawDiagnostics -source 15 MinContextOpTest.java
* @compile/fail/ref=MinContextOpTest_B.out -XDrawDiagnostics MinContextOpTest.java
*/ */
import java.util.Map; import java.util.Map;

View File

@ -0,0 +1,6 @@
- compiler.warn.source.no.system.modules.path: 15
MinContextOpTest.java:39:25: compiler.err.mod.not.allowed.here: static
MinContextOpTest.java:45:25: compiler.err.mod.not.allowed.here: static
MinContextOpTest.java:51:34: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: T,K,V,E, (compiler.misc.inconvertible.types: java.util.function.Function<MinContextOpTest.A.T,MinContextOpTest.A.T>, java.util.function.Function<? super MinContextOpTest.A.T,? extends MinContextOpTest.A.T<?>>))
3 errors
1 warning

View File

@ -1,4 +1,2 @@
MinContextOpTest.java:38:25: compiler.err.mod.not.allowed.here: static MinContextOpTest.java:51:34: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: T,K,V,E, (compiler.misc.inconvertible.types: java.util.function.Function<MinContextOpTest.A.T,MinContextOpTest.A.T>, java.util.function.Function<? super MinContextOpTest.A.T,? extends MinContextOpTest.A.T<?>>))
MinContextOpTest.java:44:25: compiler.err.mod.not.allowed.here: static 1 error
MinContextOpTest.java:50:34: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: T,K,V,E, (compiler.misc.inconvertible.types: java.util.function.Function<MinContextOpTest.A.T,MinContextOpTest.A.T>, java.util.function.Function<? super MinContextOpTest.A.T,? extends MinContextOpTest.A.T<?>>))
3 errors

View File

@ -22,6 +22,8 @@
*/ */
// key: compiler.err.static.declaration.not.allowed.in.inner.classes // key: compiler.err.static.declaration.not.allowed.in.inner.classes
// key: compiler.warn.source.no.system.modules.path
// options: -source 15
class EnumsMustBeStatic { class EnumsMustBeStatic {
class Nested { class Nested {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2010, 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
@ -22,6 +22,8 @@
*/ */
// key: compiler.err.icls.cant.have.static.decl // key: compiler.err.icls.cant.have.static.decl
// key: compiler.warn.source.no.system.modules.path
// options: -source 15
class InnerClassCantHaveStatic { class InnerClassCantHaveStatic {
class Inner { class Inner {

View File

@ -1,30 +0,0 @@
/*
* 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.
*/
// key: compiler.err.static.declaration.not.allowed.in.inner.classes
class Outer {
class Inner {
record R(int a) {}
}
}

View File

@ -1,10 +1,11 @@
/* /*
* @test /nodynamiccopyright/ * @test /nodynamiccopyright/
* @bug 5071831 * @bug 5071831
* @summary javac allows enum in an inner class * @summary javac allows enum in an inner class for source >= 16
* @author gafter * @author gafter
* *
* @compile/fail/ref=NestedEnum.out -XDrawDiagnostics NestedEnum.java * @compile/fail/ref=NestedEnum.out -XDrawDiagnostics -source 15 NestedEnum.java
* @compile NestedEnum.java
*/ */
class NestedEnum { class NestedEnum {

View File

@ -1,2 +1,4 @@
NestedEnum.java:12:9: compiler.err.static.declaration.not.allowed.in.inner.classes - compiler.warn.source.no.system.modules.path: 15
NestedEnum.java:13:9: compiler.err.static.declaration.not.allowed.in.inner.classes
1 error 1 error
1 warning

View File

@ -1,9 +1,10 @@
/* /*
* @test /nodynamiccopyright/ * @test /nodynamiccopyright/
* @bug 5081785 * @bug 5081785
* @summary Empty Enums allowed in non-static contexts * @summary enums should be allowed in non-static contexts
* @author Peter von der Ah\u00e9 * @author Peter von der Ah\u00e9
* @compile/fail/ref=T5081785.out -XDrawDiagnostics T5081785.java * @compile/fail/ref=T5081785.out -XDrawDiagnostics -source 15 T5081785.java
* @compile T5081785.java
*/ */
class A1 { class A1 {

View File

@ -1,5 +1,7 @@
T5081785.java:29:9: compiler.err.static.declaration.not.allowed.in.inner.classes - compiler.warn.source.no.system.modules.path: 15
T5081785.java:12:13: compiler.err.static.declaration.not.allowed.in.inner.classes T5081785.java:30:9: compiler.err.static.declaration.not.allowed.in.inner.classes
T5081785.java:19:27: compiler.err.static.declaration.not.allowed.in.inner.classes T5081785.java:13:13: compiler.err.static.declaration.not.allowed.in.inner.classes
T5081785.java:24:31: compiler.err.static.declaration.not.allowed.in.inner.classes T5081785.java:20:27: compiler.err.static.declaration.not.allowed.in.inner.classes
T5081785.java:25:31: compiler.err.static.declaration.not.allowed.in.inner.classes
4 errors 4 errors
1 warning

View File

@ -48,6 +48,12 @@ import combo.ComboTask;
import combo.ComboTask.Result; import combo.ComboTask.Result;
import combo.ComboTestHelper; import combo.ComboTestHelper;
/** this test checks two thinks:
* 1 - that static declarations are allowed inside inner classes
* 2 - and in addtion that non-static variables can't be captured
* by static contexts
*/
public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclarations> { public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclarations> {
static final String sourceTemplate = static final String sourceTemplate =
@ -57,10 +63,12 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
int INSTANCE_FIELD = 0; int INSTANCE_FIELD = 0;
static int STATIC_FIELD = 0; static int STATIC_FIELD = 0;
// instance initializer // instance initializer
{ int LOCAL_VARIABLE = 0; {
int LOCAL_VARIABLE = 0;
#{CONTAINER} #{CONTAINER}
} }
Test() { Test() {
int LOCAL_VARIABLE = 0;
#{CONTAINER} #{CONTAINER}
} }
void m() { void m() {
@ -93,7 +101,7 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
), ),
RECORD("record CR() { #{STATIC_LOCAL} }"), RECORD("record CR() { #{STATIC_LOCAL} }"),
CLASS("class CC { #{STATIC_LOCAL} }"), CLASS("class CC { #{STATIC_LOCAL} }"),
ENUM("enum CE { #{STATIC_LOCAL} }"), ENUM("enum CE { CE1; #{STATIC_LOCAL} }"),
LAMBDA("Runnable run = () -> { #{STATIC_LOCAL} };"); LAMBDA("Runnable run = () -> { #{STATIC_LOCAL} };");
String container; String container;
@ -124,7 +132,6 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
} }
enum Member implements ComboParameter { enum Member implements ComboParameter {
NONE(""),
METHOD("int foo() { return #{EXPR}; }"), METHOD("int foo() { return #{EXPR}; }"),
DEFAULT_METHOD("default int foo() { return #{EXPR}; }"); DEFAULT_METHOD("default int foo() { return #{EXPR}; }");
@ -179,13 +186,13 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
} }
boolean notTriviallyIncorrect() { boolean notTriviallyIncorrect() {
return decl == StaticLocalDecl.INTERFACE && (member == Member.DEFAULT_METHOD || member == Member.NONE) || return decl == StaticLocalDecl.INTERFACE && member == Member.DEFAULT_METHOD ||
decl != StaticLocalDecl.INTERFACE && (member == Member.METHOD || member == Member.NONE); decl != StaticLocalDecl.INTERFACE && member == Member.METHOD;
} }
void check(ComboTask.Result<Iterable<? extends JavaFileObject>> result) { void check(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
if (shouldFail()) { if (shouldFail()) {
Assert.check(result.hasErrors(), result.compilationInfo()); Assert.check(result.hasErrors(), "unexpected compilation\n" + result.compilationInfo());
if (!expectedDiagFound(result)) { if (!expectedDiagFound(result)) {
fail("test failing with unexpected error message\n" + result.compilationInfo()); fail("test failing with unexpected error message\n" + result.compilationInfo());
} }
@ -195,10 +202,7 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
} }
boolean shouldFail() { boolean shouldFail() {
return ((container != Container.NO_CONTAINER && return (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD);
container != Container.LAMBDA &&
container != Container.ANONYMOUS)) ||
(member != Member.NONE && !acceptableExpr());
} }
boolean acceptableExpr() { boolean acceptableExpr() {
@ -206,14 +210,9 @@ public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclaratio
} }
boolean expectedDiagFound(ComboTask.Result<Iterable<? extends JavaFileObject>> result) { boolean expectedDiagFound(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
if ((container == Container.NO_CONTAINER || if (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD) {
container == Container.LAMBDA ||
container == Container.ANONYMOUS) &&
!acceptableExpr()) {
return result.containsKey("compiler.err.non-static.cant.be.ref"); return result.containsKey("compiler.err.non-static.cant.be.ref");
} else if (container == Container.ENUM) {
return result.containsKey("compiler.err.enum.constant.expected" );
} }
return result.containsKey("compiler.err.static.declaration.not.allowed.in.inner.classes" ); return false;
} }
} }

View File

@ -477,365 +477,483 @@ public class RecordCompilationTests extends CompilationTestCase {
assertFail("compiler.err.already.defined", template); assertFail("compiler.err.already.defined", template);
} }
public void testStaticLocalTypes() { public void testStaticLocals() {
// local records can also be final // static locals can't capture local variables, instance fields or type variables
assertOK("class R { \n" + for (String s : List.of(
" void m() { \n" + "record RR(int x) { public int x() { return y; }};",
" final record RR(int x) { };\n" + "record RR(int x) { public int x() { return z; }};",
" }\n" + "record RR(int x) { public int x() { return instance; }};",
"}"); "record RR(T t) {};",
"record RR(U u) {};",
// Can't capture locals "interface I { default int x() { return y; }};",
assertFail("compiler.err.non-static.cant.be.ref", "interface I { default int x() { return z; }};",
"class R { \n" + "interface I { default int x() { return instance; }};",
" void m(int y) { \n" + "interface I { default int x(T t) { return 0; }};",
" record RR(int x) { public int x() { return y; }};\n" + "interface I { default int x(U u) { return 0; }};",
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref", "enum E { A; int x() { return y; }};",
"class R { \n" + "enum E { A; int x() { return z; }};",
" void m() {\n" + "enum E { A; int x() { return instance; }};",
" int y;\n" + "enum E { A; int x(T t) { return 0; }};",
" record RR(int x) { public int x() { return y; }};\n" + "enum E { A; int x(U u) { return 0; }};"
" }\n" + )) {
"}"); assertFail("compiler.err.non-static.cant.be.ref",
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m() {\n" +
" String hello = \"hello\";\n" +
" interface I {\n" +
" public default void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(hello);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m() {\n" +
" String hello = \"hello\";\n" +
" record R(int i) {\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(hello);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m() {\n" +
" String hello = \"hello\";\n" +
" enum E {\n" +
" A;\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(hello);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m(String param) {\n" +
" interface I {\n" +
" public default void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(param);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m(String param) {\n" +
" record R(int i) {\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(param);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static void m(String param) {\n" +
" enum E {\n" +
" A;\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(param);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" String instanceField = \"instance\";\n" +
" public static void m() {\n" +
" interface I {\n" +
" public default void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(instanceField);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" String instanceField = \"instance\";\n" +
" public static void m(String param) {\n" +
" record R(int i) {\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(instanceField);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" String instanceField = \"instance\";\n" +
" public static void m(String param) {\n" +
" enum E {\n" +
" A;\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" System.err.println(instanceField);\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
// instance fields
assertFail("compiler.err.non-static.cant.be.ref",
"class R { \n" +
" int z = 0;\n" +
" void m() { \n" +
" record RR(int x) { public int x() { return z; }};\n" +
" }\n" +
"}");
// or type variables
assertFail("compiler.err.non-static.cant.be.ref",
"class R<T> { \n" +
" void m() { \n" +
" record RR(T t) {};\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class R {\n" +
" static <U> U make(U u) { //method is static\n" +
" interface Checker {\n" +
" void check(U u);\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class LocalEnum {\n" +
" static <U> U getAndSet(U u) { //method is static\n" +
" enum X {\n" +
" A;\n" +
" U u;\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}\n");
assertFail("compiler.err.non-static.cant.be.ref",
"class R {\n" +
" static <U> U make(U u) { //method is static\n" +
" record Checker() {\n" +
" void check(U u);\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class R {\n" +
" <U> U make(U u) { // enclosing method is not static\n" +
" interface Checker {\n" +
" void check(U u);\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class LocalEnum {\n" +
" <U> U getAndSet(U u) { // enclosing method is not static\n" +
" enum X {\n" +
" A;\n" +
" U u;\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}\n");
assertFail("compiler.err.non-static.cant.be.ref",
"class R {\n" +
" <U> U make(U u) { // enclosing method is not static\n" +
" record Checker() {\n" +
" void check(U u);\n" +
" }\n" +
" return null;\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static <T> void main(String[] args) {\n" +
" interface I {\n" +
" public default void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" T t = null;\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static <T> void main(String[] args) {\n" +
" record R(int i) {\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" T t = null;\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
assertFail("compiler.err.non-static.cant.be.ref",
"class C {\n" +
" public static <T> void main(String[] args) {\n" +
" enum E {\n" +
" A;\n" +
" public void test1() {\n" +
" class X {\n" +
" public void test2() {\n" +
" T t = null;\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
// but static fields are OK
assertOK("class R { \n" +
" static int z = 0;\n" +
" void m() { \n" +
" record RR(int x) { public int x() { return z; }};\n" +
" }\n" +
"}");
// Can't self-shadow
assertFail("compiler.err.already.defined",
""" """
class R { class R<T> {
void m() { int instance = 0;
record R(int x) { }; <U> U m(int y) {
int z;
#S
return null;
} }
} }
""" """.replaceFirst("#S", s));
); }
// a similar example but a bit more complex
for (String s : List.of(
"record R() { void test1() { class X { void test2() { System.err.println(localVar); } } } }",
"record R() { void test1() { class X { void test2() { System.err.println(param); } } } }",
"record R() {void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
"record R() { void test1() { class X { T t; } } }",
"record R() { void test1() { class X { U u; } } }",
"interface I { default void test1() { class X { void test2() { System.err.println(localVar); } } } }",
"interface I() { default void test1() { class X { void test2() {System.err.println(param);} } } }",
"interface I { default void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
"interface I { default void test1() { class X { T t; } } }",
"interface I() { default void test1() { class X {U u;} } }",
"enum E { A; void test1() { class X { void test2() { System.err.println(localVar); } } } }",
"enum E { A; void test1() { class X { void test2() {System.err.println(param);} } } }",
"enum E { A; void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
"enum E { A; void test1() { class X { T t; } } }",
"enum E { A; void test1() { class X {U u;} } }"
)) {
assertFail("compiler.err.non-static.cant.be.ref",
"""
class C<T> {
String instanceField = "instance";
static <U> U m(String param) {
String localVar = "local";
#S
return null;
}
}
""".replaceFirst("#S", s));
}
// can't self-shadow
for (String s : List.of("record R() {}", "interface R {}", "enum R { A }")) {
assertFail("compiler.err.already.defined", "class R { void m() { #S } }".replaceFirst("#S", s));
}
// can't be explicitly static // can't be explicitly static
assertFail("compiler.err.illegal.start.of.expr", for (String s : List.of("static record RR() { }", "static interface I {}", "static enum E { A }")) {
assertFail("compiler.err.illegal.start.of.expr", "class R { void m() { #S } }".replaceFirst("#S", s));
}
// but static fields can be accessed
for (String s : List.of(
"record RR() { public int x() { return z; } };",
"interface I { default int x() { return z; } }",
"enum E { A; int x() { return z; } }"
)) {
assertOK("class R { static int z = 0; void m() { #S } }".replaceFirst("#S", s));
}
// local records can also be final
assertOK("class R { void m() { final record RR(int x) { }; } }");
}
public void testStaticDefinitionsInInnerClasses() {
// static defs in inner classes can't capture instance fields or type variables
for (String s : List.of(
""" """
class R { record R() {
void m() { void test() { System.err.println(field); }
static record RR(int x) { }; }
""",
"""
record R() {
void test(T t) {}
}
""",
"""
record R() {
void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
record R() {
void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
interface I {
default void test() { System.err.println(field); }
}
""",
"""
interface I {
default void test(T t) {}
}
""",
"""
interface I {
default void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
interface I {
default void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
enum E {
A;
void test() { System.err.println(field); }
}
""",
"""
enum E {
A;
void test(T t) {}
}
""",
"""
enum E {
A;
void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
enum E {
A;
void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
static class SC {
void test() { System.err.println(field); }
}
""",
"""
static class SC {
void test(T t) {}
}
""",
"""
static class SC {
void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
static class SC {
void test1() {
class X { void test2(T t) {} }
} }
} }
""" """
); )) {
assertFail("compiler.err.non-static.cant.be.ref",
"""
class C<T> {
String field = "field";
class Inner {
#S
}
}
""".replaceFirst("#S", s));
}
// positive cases // another, more complex, example
assertOK( // static defs in inner classes can't capture instance locals, fields or type variables
for (String s : List.of(
""" """
import java.security.*; record R() {
class Test { void test() { System.err.println(field); }
static Test newInstance(Object provider) { }
return new Test() { """,
private final PrivilegedExceptionAction<KeyStore> action = new PrivilegedExceptionAction<KeyStore>() { """
public KeyStore run() throws Exception { record R() {
if (provider == null) {} void test1() {
return null; class X { void test2() { System.err.println(field); } }
} }
}; }
}; """,
"""
record R() {
void test() { System.err.println(param); }
}
""",
"""
record R() {
void test1() {
class X { void test2() { System.err.println(param); } }
}
}
""",
"""
record R() {
void test() { System.err.println(local); }
}
""",
"""
record R() {
void test1() {
class X { void test2() { System.err.println(local); } }
}
}
""",
"""
record R() {
void test(T t) {}
}
""",
"""
record R() {
void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
interface I {
default void test() { System.err.println(field); }
}
""",
"""
interface I {
default void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
interface I {
default void test() { System.err.println(param); }
}
""",
"""
interface I {
default void test1() {
class X {
void test2() { System.err.println(param); }
}
}
}
""",
"""
interface I {
default void test() { System.err.println(local); }
}
""",
"""
interface I {
default void test1() {
class X {
void test2() { System.err.println(local); }
}
}
}
""",
"""
interface I {
default void test(T t) {}
}
""",
"""
interface I {
default void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
enum E {
A;
void test() { System.err.println(field); }
}
""",
"""
enum E {
A;
void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
enum E {
A;
void test() { System.err.println(param); }
}
""",
"""
enum E {
A;
void test1() {
class X {
void test2() { System.err.println(param); }
}
}
}
""",
"""
enum E {
A;
void test() { System.err.println(local); }
}
""",
"""
enum E {
A;
void test1() {
class X {
void test2() { System.err.println(local); }
}
}
}
""",
"""
enum E {
A;
void test(T t) {}
}
""",
"""
enum E {
A;
void test1() {
class X { void test2(T t) {} }
}
}
""",
"""
static class SC {
void test() { System.err.println(field); }
}
""",
"""
static class SC {
void test1() {
class X {
void test2() { System.err.println(field); }
}
}
}
""",
"""
static class SC {
void test() { System.err.println(param); }
}
""",
"""
static class SC {
void test1() {
class X {
void test2() { System.err.println(param); }
}
}
}
""",
"""
static class SC {
void test() { System.err.println(local); }
}
""",
"""
static class SC {
void test1() {
class X {
void test2() { System.err.println(local); }
}
}
}
""",
"""
static class SC {
void test(T t) {}
}
""",
"""
static class SC {
void test1() {
class X { void test2(T t) {} }
} }
} }
""" """
); )) {
assertFail("compiler.err.non-static.cant.be.ref",
assertOK( """
""" class C<T> {
import java.security.*; String field = "field";
class Test { <U> U m(String param) {
static Test newInstance(Object provider) { String local = "local";
return new Test() { class Local {
int m(PrivilegedExceptionAction<KeyStore> a) { return 0; } class Inner { #S }
{
m(
new PrivilegedExceptionAction<KeyStore>() {
public KeyStore run() throws Exception {
if (provider == null) {}
return null;
}
}
);
} }
}; return null;
}
} }
""".replaceFirst("#S", s));
}
// inner classes can contain static methods too
assertOK(
"""
class C {
class Inner {
// static method inside inner class
static void m() {}
}
}
"""
);
assertOK(
"""
class C {
void m() {
new Object() {
// static method inside inner class
static void m() {}
};
}
} }
""" """
); );
@ -869,7 +987,7 @@ public class RecordCompilationTests extends CompilationTestCase {
} }
public void testRecordsInsideInner() { public void testRecordsInsideInner() {
assertFail("compiler.err.static.declaration.not.allowed.in.inner.classes", assertOK(
""" """
class Outer { class Outer {
class Inner { class Inner {
@ -878,7 +996,7 @@ public class RecordCompilationTests extends CompilationTestCase {
} }
""" """
); );
assertFail("compiler.err.static.declaration.not.allowed.in.inner.classes", assertOK(
""" """
class Outer { class Outer {
public void test() { public void test() {
@ -888,7 +1006,7 @@ public class RecordCompilationTests extends CompilationTestCase {
} }
} }
"""); """);
assertFail("compiler.err.static.declaration.not.allowed.in.inner.classes", assertOK(
""" """
class Outer { class Outer {
Runnable run = new Runnable() { Runnable run = new Runnable() {
@ -897,7 +1015,7 @@ public class RecordCompilationTests extends CompilationTestCase {
}; };
} }
"""); """);
assertFail("compiler.err.static.declaration.not.allowed.in.inner.classes", assertOK(
""" """
class Outer { class Outer {
void m() { void m() {