8247790: javac shouldn't allow type variable references from local static declarations

Reviewed-by: jlahoda
This commit is contained in:
Vicente Romero 2020-07-15 22:36:45 -04:00
parent fd206e1e7e
commit bcd4690bc3
2 changed files with 285 additions and 9 deletions
src/jdk.compiler/share/classes/com/sun/tools/javac/comp
test/langtools/tools/javac/records

@ -105,6 +105,7 @@ public class Resolve {
JCDiagnostic.Factory diags;
public final boolean allowFunctionalInterfaceMostSpecific;
public final boolean allowModules;
public final boolean allowRecords;
public final boolean checkVarargsAccessAfterResolution;
private final boolean compactMethodDiags;
private final boolean allowLocalVariableTypeInference;
@ -147,6 +148,8 @@ public class Resolve {
Feature.POST_APPLICABILITY_VARARGS_ACCESS_CHECK.allowedInSource(source);
polymorphicSignatureScope = WriteableScope.create(syms.noSymbol);
allowModules = Feature.MODULES.allowedInSource(source);
allowRecords = (!preview.isPreview(Feature.RECORDS) || preview.isEnabled()) &&
Feature.RECORDS.allowedInSource(source);
}
/** error symbols, which are returned when resolution fails
@ -1488,15 +1491,18 @@ public class Resolve {
}
if (sym.exists()) {
if (staticOnly &&
(sym.flags() & STATIC) == 0 &&
sym.kind == VAR &&
// if it is a field
(sym.owner.kind == TYP ||
// or it is a local variable but it is not declared inside of the static local type
// only records so far, then error
// then error
allowRecords &&
(sym.owner.kind == MTH) &&
(env.enclClass.sym.flags() & STATIC) != 0 &&
sym.enclClass() != env.enclClass.sym) &&
(sym.flags() & STATIC) == 0)
env1 != env &&
!isInnerClassOfMethod(sym.owner, env.tree.hasTag(CLASSDEF) ?
((JCClassDecl)env.tree).sym :
env.enclClass.sym)))
return new StaticError(sym);
else
return sym;
@ -2261,19 +2267,37 @@ public class Resolve {
return bestSoFar;
}
Symbol findTypeVar(Env<AttrContext> env, Name name, boolean staticOnly) {
for (Symbol sym : env.info.scope.getSymbolsByName(name)) {
Symbol findTypeVar(Env<AttrContext> currentEnv, Env<AttrContext> originalEnv, Name name, boolean staticOnly) {
for (Symbol sym : currentEnv.info.scope.getSymbolsByName(name)) {
if (sym.kind == TYP) {
if (staticOnly &&
sym.type.hasTag(TYPEVAR) &&
sym.owner.kind == TYP)
((sym.owner.kind == TYP) ||
// are we trying to access a TypeVar defined in a method from a local static type: interface, enum or record?
allowRecords &&
(sym.owner.kind == MTH &&
currentEnv != originalEnv &&
!isInnerClassOfMethod(sym.owner, originalEnv.tree.hasTag(CLASSDEF) ?
((JCClassDecl)originalEnv.tree).sym :
originalEnv.enclClass.sym)))) {
return new StaticError(sym);
}
return sym;
}
}
return typeNotFound;
}
boolean isInnerClassOfMethod(Symbol msym, Symbol csym) {
if (csym.owner == msym && !csym.isStatic()) {
return true;
} else if (csym.owner.kind == TYP) {
return isInnerClassOfMethod(msym, csym.owner);
} else {
return false;
}
}
/** Find an unqualified type symbol.
* @param env The current environment.
* @param name The type's name.
@ -2287,7 +2311,7 @@ public class Resolve {
for (Env<AttrContext> env1 = env; env1.outer != null; env1 = env1.outer) {
if (isStatic(env1)) staticOnly = true;
// First, look for a type variable and the first member type
final Symbol tyvar = findTypeVar(env1, name, staticOnly);
final Symbol tyvar = findTypeVar(env1, env, name, staticOnly);
sym = findImmediateMemberType(env1, env1.enclClass.sym.type,
name, env1.enclClass.sym);

@ -488,7 +488,7 @@ public class RecordCompilationTests extends CompilationTestCase {
" }\n" +
"}");
// Cant capture locals
// Can't capture locals
assertFail("compiler.err.non-static.cant.be.ref",
"class R { \n" +
" void m(int y) { \n" +
@ -504,6 +504,150 @@ public class RecordCompilationTests extends CompilationTestCase {
" }\n" +
"}");
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" +
@ -521,6 +665,114 @@ public class RecordCompilationTests extends CompilationTestCase {
" }\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" +