New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8263614: javac allows local variables to be accessed from a static context #4004
8263614: javac allows local variables to be accessed from a static context #4004
Conversation
…d in a local class generates an NPE while compiling
|
@vicente-romero-oracle The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
This is a tricky area, so I played a little with the (unchanged) code, to get an idea of what was wrong. It seems to me that the behavior of
This doesn't seem too bad - but there is an issue, e.g. in the example mentioned in the JBS issue: we look into locals (step 1) regardless of the value of To rectify this issue, I came up with the following, relatively minimal fix:
The idea here is that, whatever symbol is found during step (1), if It is likely that, as in your patch, similar changes would be needed for other routines such as |
@mcimadamore, I have uploaded another commit which builds on your patch and adds a small change to Resolve::findTypeVar, thanks |
!isInnerClassOfMethod(sym.owner, originalEnv.tree.hasTag(CLASSDEF) ? | ||
((JCClassDecl)originalEnv.tree).sym : | ||
originalEnv.enclClass.sym)))) { | ||
(!isInnerClassOfMethod(sym.owner, originalEnv.tree.hasTag(CLASSDEF) ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you do a similar approach here and use staticOnly to rule out tvars?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can go in findType, move the assignment to staticOnly to after the call to findTypeVar - then you can drop all type var symbol you find with staticOnly = true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that type variables are trickier than local variables and probably won't be as easy to tame. I tried several combinations of the patch below which I think is what you have in mind:
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
index 70ea875a4b3..832a8ba8ede 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
@@ -2316,29 +2316,6 @@ public class Resolve {
return bestSoFar;
}
- 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) ||
- // 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) ||
- originalEnv.info.staticLevel > currentEnv.info.staticLevel)
- ))) {
- return new StaticError(sym);
- }
- return sym;
- }
- }
- return typeNotFound;
- }
-
boolean isInnerClassOfMethod(Symbol msym, Symbol csym) {
while (csym.owner != msym) {
if (csym.isStatic()) return false;
@@ -2358,18 +2335,25 @@ public class Resolve {
Symbol sym;
boolean staticOnly = false;
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, env, name, staticOnly);
+ Symbol tyvar = typeNotFound;
+ for (Symbol s : env1.info.scope.getSymbolsByName(name)) {
+ if (s.kind == TYP) {
+ tyvar = s;
+ }
+ }
sym = findImmediateMemberType(env1, env1.enclClass.sym.type,
name, env1.enclClass.sym);
-
+ if (isStatic(env1)) staticOnly = true;
// Return the type variable if we have it, and have no
// immediate member, OR the type variable is for a method.
if (tyvar != typeNotFound) {
if (env.baseClause || sym == typeNotFound ||
(tyvar.kind == TYP && tyvar.exists() &&
tyvar.owner.kind == MTH)) {
+ if (staticOnly) {
+ return new StaticError(tyvar);
+ }
return tyvar;
}
}
if the line: if (isStatic(env1)) staticOnly = true;
is left in the position it appears in the patch above then valid code like:
public static <A> List<A> nil() {
return (List<A>)EMPTY_LIST;
}
will stop being accepted as the type variable is being accessed from a static context, but this case is kosher. On the other hand if line if (isStatic(env1)) staticOnly = true;
if moved after the if block:
if (tyvar != typeNotFound) {
then the compiler will accept the code below when it should fail:
import java.util.Collection;
class Test<T extends Collection> {
static void test(T t) { t.iterator(); }
}
I'm doing more investigation. The code for resolving types is definitively complex. It seems to me that the complexity comes from 3 problems: Problem 1:
Here the env for Problem 2:
Here the env for Problem 3:
This should obviously work, although, in compiler terms, there's not much difference between (3) and (2). I'm currently looking into all the changes made in this area in recent times, and see if there's an easier path to support all this. |
This is what I came up with:
It reverts some of the code changes made for records, and reverts the code to a simpler state (the isInnerClassOfMethod) is no longer needed. This passes all tests, including the modified RecordCompilationTests. |
@vicente-romero-oracle This change now passes all automated pre-integration checks. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 137 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.
|
/integrate |
@vicente-romero-oracle Since your change was applied there have been 137 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit b8856b1. |
javac breaks with NPE if compiles this code:
actually the compiler should issue a compiling error as local variable
i
is being accessed from a static context. Please review this fix to address this issue. Some notes:com.sun.tools.javac.comp.AttrContext.staticLevel
keeps a the number of nested static contexts but this calculation doesn't consider static type declarations. This is because static declaration doesn't introduce a static context. But if they have a static modifier, even if implicit as it is for local records, then this affects what variables, type variables, etc are accessible from the body of the static type declaration. For this reason I have used anadjusted
static level that takes static type declarations into account.TIA
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/4004/head:pull/4004
$ git checkout pull/4004
Update a local copy of the PR:
$ git checkout pull/4004
$ git pull https://git.openjdk.java.net/jdk pull/4004/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 4004
View PR using the GUI difftool:
$ git pr show -t 4004
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/4004.diff