From fc30c88bc5a5c6e0db17571bda3214b07ad2412a Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 12 Jun 2025 13:19:57 -0400 Subject: [PATCH 01/19] 8359370: [lworld] allow instance fields of identity classes to be readable in the prologue phase --- .../classes/com/sun/tools/javac/comp/Resolve.java | 12 +++++------- .../tools/javac/SuperInit/SuperInitFails.out | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) 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 ad7a66ae7ee..955826160a8 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 @@ -1538,13 +1538,11 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { if (staticOnly) return new StaticError(sym); if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbol)sym)) { - if (!env.tree.hasTag(ASSIGN) || !TreeInfo.isIdentOrThisDotIdent(((JCAssign)env.tree).lhs)) { - if (!sym.isStrictInstance()) { - return new RefBeforeCtorCalledError(sym); - } else { - localProxyVarsGen.addStrictFieldReadInPrologue(env.enclMethod, sym); - return sym; - } + if (!sym.isStrictInstance()) { + return new RefBeforeCtorCalledError(sym); + } else { + localProxyVarsGen.addStrictFieldReadInPrologue(env.enclMethod, sym); + return sym; } } } diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out index 4394a6741c3..a6cabb6f5ed 100644 --- a/test/langtools/tools/javac/SuperInit/SuperInitFails.out +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out @@ -18,6 +18,7 @@ SuperInitFails.java:168:13: compiler.err.cant.ref.before.ctor.called: x SuperInitFails.java:172:17: compiler.err.cant.ref.before.ctor.called: x SuperInitFails.java:176:24: compiler.err.cant.ref.before.ctor.called: x SuperInitFails.java:180:18: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:186:28: compiler.err.cant.ref.before.ctor.called: this SuperInitFails.java:195:25: compiler.err.return.before.superclass.initialized SuperInitFails.java:200:33: compiler.err.ctor.calls.not.allowed.here SuperInitFails.java:205:29: compiler.err.redundant.superclass.init @@ -32,4 +33,4 @@ SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized SuperInitFails.java:150:18: compiler.err.call.must.only.appear.in.ctor - compiler.note.preview.filename: SuperInitFails.java, DEFAULT - compiler.note.preview.recompile -32 errors +33 errors From 17bf7b5cdfef855c0b714e2ff4a642873a32024d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 17 Jun 2025 11:06:35 -0400 Subject: [PATCH 02/19] initial approach --- .../com/sun/tools/javac/comp/Attr.java | 3 +++ .../com/sun/tools/javac/comp/AttrContext.java | 6 +++++ .../sun/tools/javac/comp/CompileStates.java | 2 +- .../com/sun/tools/javac/comp/Flow.java | 22 +++++++++++----- .../tools/javac/comp/LocalProxyVarsGen.java | 26 +++++++++---------- .../com/sun/tools/javac/comp/Resolve.java | 9 ++++--- .../sun/tools/javac/main/JavaCompiler.java | 25 ++++++------------ .../javac/SuperInit/EarlyAssignments2.java | 20 ++++++++++++++ .../javac/SuperInit/EarlyAssignments2.out | 0 9 files changed, 72 insertions(+), 41 deletions(-) create mode 100644 test/langtools/tools/javac/SuperInit/EarlyAssignments2.java create mode 100644 test/langtools/tools/javac/SuperInit/EarlyAssignments2.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index cc0e5c38e58..04c52ade2e5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -958,7 +958,9 @@ public void visitClassDef(JCClassDecl tree) { Optional.ofNullable(env.info.attributionMode.isSpeculative ? argumentAttr.withLocalCacheContext() : null); boolean ctorProloguePrev = env.info.ctorPrologue; + boolean isLocalClassPrev = env.info.isLocalClass; try { + env.info.isLocalClass = env.enclMethod != null; // Local and anonymous classes have not been entered yet, so we need to // do it now. if (env.info.scope.owner.kind.matches(KindSelector.VAL_MTH)) { @@ -992,6 +994,7 @@ public void visitClassDef(JCClassDecl tree) { } finally { localCacheContext.ifPresent(LocalCacheContext::leave); env.info.ctorPrologue = ctorProloguePrev; + env.info.isLocalClass = isLocalClassPrev; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java index 6f6da2829a7..16325711d3b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java @@ -132,6 +132,11 @@ public class AttrContext { boolean instanceInitializerBlock = false; + /** + * Is this an attribution environment for a local class + */ + boolean isLocalClass = false; + /** Duplicate this context, replacing scope field and copying all others. */ AttrContext dup(WriteableScope scope) { @@ -157,6 +162,7 @@ AttrContext dup(WriteableScope scope) { info.allowProtectedAccess = allowProtectedAccess; info.instanceInitializerBlock = instanceInitializerBlock; info.isPermitsClause = isPermitsClause; + info.isLocalClass = isLocalClass; return info; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java index 7cc506440ed..26c8bb7dbcf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java @@ -63,7 +63,7 @@ public enum CompileState { TRANSPATTERNS(8), LOWER(9), UNLAMBDA(10), - STRICT_FIELDS_PROXIES(11), + FIELDS_PROXIES(11), GENERATE(12); CompileState(int value) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index b4d8f4ae120..6a9d2f16d3a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -2205,6 +2205,7 @@ public AssignAnalyzer() { private boolean isConstructor; private boolean isCompactOrGeneratedRecordConstructor; + private boolean ctorPrologue; @Override protected void markDead() { @@ -2221,13 +2222,13 @@ protected boolean trackable(VarSymbol sym) { return sym.pos >= startPos && ((sym.owner.kind == MTH || sym.owner.kind == VAR || - isFinalOrStrictUninitializedField(sym))); + isFinalOrUninitializedField(sym))); } - boolean isFinalOrStrictUninitializedField(VarSymbol sym) { + boolean isFinalOrUninitializedField(VarSymbol sym) { return sym.owner.kind == TYP && (((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL || - (sym.flags() & (STRICT | HASINIT | PARAMETER)) == STRICT) && + (allowValueClasses && (sym.flags() & (HASINIT | PARAMETER)) == 0)) && classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); } @@ -2329,8 +2330,10 @@ void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { trackable(sym) && !inits.isMember(sym.adr) && (sym.flags_field & CLASH) == 0) { - log.error(pos, errkey); - inits.incl(sym.adr); + if ((!allowValueClasses || (allowValueClasses && ctorPrologue && ((sym.flags() & (HASINIT | PARAMETER))) == 0))) { + log.error(pos, errkey); + inits.incl(sym.adr); + } } } @@ -2528,10 +2531,12 @@ public void visitMethodDef(JCMethodDecl tree) { Assert.check(pendingExits.isEmpty()); boolean isConstructorPrev = isConstructor; boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor; + boolean ctorProloguePrev = ctorPrologue; try { isConstructor = TreeInfo.isConstructor(tree); isCompactOrGeneratedRecordConstructor = isConstructor && ((tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD)); + ctorPrologue = isConstructor; // We only track field initialization inside constructors if (!isConstructor) { @@ -2603,6 +2608,7 @@ public void visitMethodDef(JCMethodDecl tree) { returnadr = returnadrPrev; isConstructor = isConstructorPrev; isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev; + ctorPrologue = ctorProloguePrev; } } finally { lint = lintPrev; @@ -3112,13 +3118,14 @@ public void visitApply(JCMethodInvocation tree) { scan(def); clearPendingExits(false); }); + ctorPrologue = false; } // If this(): at this point all final uninitialized fields will get initialized else if (name == names._this) { for (int address = firstadr; address < nextadr; address++) { VarSymbol sym = vardecls[address].sym; - if (isFinalOrStrictUninitializedField(sym) && !sym.isStatic()) + if (isFinalOrUninitializedField(sym) && !sym.isStatic()) letInit(tree.pos(), sym); } } @@ -3199,7 +3206,8 @@ public void visitAssign(JCAssign tree) { // assigned before reading their value public void visitSelect(JCFieldAccess tree) { super.visitSelect(tree); - if (TreeInfo.isThisQualifier(tree.selected) && + if ((TreeInfo.isThisQualifier(tree.selected) || + (classDef != null && TreeInfo.isExplicitThisReference(types, (Type.ClassType)classDef.type, tree.selected))) && tree.sym.kind == VAR) { checkInit(tree.pos(), (VarSymbol)tree.sym); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java index dea2dbfaf66..e1465b8bce0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java @@ -88,8 +88,8 @@ public static LocalProxyVarsGen instance(Context context) { private TreeMaker make; private final UnsetFieldsInfo unsetFieldsInfo; private ClassSymbol currentClass = null; - private java.util.List strictInstanceFields; - private Map> strictFieldsReadInPrologue = new HashMap<>(); + private java.util.List instanceFields; + private Map> fieldsReadInPrologue = new HashMap<>(); private final boolean noLocalProxyVars; @@ -105,10 +105,10 @@ protected LocalProxyVarsGen(Context context) { noLocalProxyVars = options.isSet("noLocalProxyVars"); } - public void addStrictFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) { - Set fieldSet = strictFieldsReadInPrologue.getOrDefault(constructor, new HashSet<>()); + public void addFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) { + Set fieldSet = fieldsReadInPrologue.getOrDefault(constructor, new HashSet<>()); fieldSet.add(sym); - strictFieldsReadInPrologue.put(constructor, fieldSet); + fieldsReadInPrologue.put(constructor, fieldSet); } public JCTree translateTopLevelClass(JCTree cdef, TreeMaker make) { @@ -128,32 +128,32 @@ public JCTree translateTopLevelClass(JCTree cdef, TreeMaker make) { @Override public void visitClassDef(JCClassDecl tree) { ClassSymbol prevCurrentClass = currentClass; - java.util.List prevStrictInstanceFields = strictInstanceFields; + java.util.List instanceFieldsPrev = instanceFields; try { currentClass = tree.sym; - strictInstanceFields = tree.defs.stream() + instanceFields = tree.defs.stream() .filter(t -> t.hasTag(VARDEF)) .map(t -> (JCVariableDecl)t) - .filter(vd -> vd.sym.isStrict() && !vd.sym.isStatic()) + .filter(vd -> !vd.sym.isStatic()) .collect(List.collector()); super.visitClassDef(tree); } finally { currentClass = prevCurrentClass; - strictInstanceFields = prevStrictInstanceFields; + instanceFields = instanceFieldsPrev; } } public void visitMethodDef(JCMethodDecl tree) { - if (strictFieldsReadInPrologue.get(tree) != null) { - Set fieldSet = strictFieldsReadInPrologue.get(tree); + if (fieldsReadInPrologue.get(tree) != null) { + Set fieldSet = fieldsReadInPrologue.get(tree); java.util.List strictFieldsRead = new ArrayList<>(); - for (JCVariableDecl sfield : strictInstanceFields) { + for (JCVariableDecl sfield : instanceFields) { if (fieldSet.contains(sfield.sym)) { strictFieldsRead.add(sfield); } } addLocalProxiesFor(tree, strictFieldsRead); - strictFieldsReadInPrologue.remove(tree); + fieldsReadInPrologue.remove(tree); } super.visitMethodDef(tree); } 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 955826160a8..96618b4a574 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 @@ -106,6 +106,7 @@ public class Resolve { ModuleFinder moduleFinder; Types types; JCDiagnostic.Factory diags; + private final LocalProxyVarsGen localProxyVarsGen; public final boolean allowModules; public final boolean allowRecords; private final boolean compactMethodDiags; @@ -115,7 +116,7 @@ public class Resolve { final EnumSet verboseResolutionMode; final boolean dumpMethodReferenceSearchResults; final boolean dumpStacktraceOnError; - private final LocalProxyVarsGen localProxyVarsGen; + private final boolean allowValueClasses; WriteableScope polymorphicSignatureScope; @@ -141,6 +142,7 @@ protected Resolve(Context context) { types = Types.instance(context); diags = JCDiagnostic.Factory.instance(context); preview = Preview.instance(context); + localProxyVarsGen = LocalProxyVarsGen.instance(context); Source source = Source.instance(context); Options options = Options.instance(context); compactMethodDiags = options.isSet(Option.XDIAGS, "compact") || @@ -155,7 +157,8 @@ protected Resolve(Context context) { allowRecords = Feature.RECORDS.allowedInSource(source); dumpMethodReferenceSearchResults = options.isSet("debug.dumpMethodReferenceSearchResults"); dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); - localProxyVarsGen = LocalProxyVarsGen.instance(context); + allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) && + Feature.VALUE_CLASSES.allowedInSource(source); } /** error symbols, which are returned when resolution fails @@ -1541,7 +1544,7 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { if (!sym.isStrictInstance()) { return new RefBeforeCtorCalledError(sym); } else { - localProxyVarsGen.addStrictFieldReadInPrologue(env.enclMethod, sym); + localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, sym); return sym; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index bb8e257b17f..df2db781ef6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -55,7 +55,6 @@ import com.sun.source.util.TaskEvent; import com.sun.tools.javac.api.MultiTaskListener; import com.sun.tools.javac.code.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.CompletionFailure; @@ -80,7 +79,6 @@ import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.Factory; import com.sun.tools.javac.util.Log.DiagnosticHandler; -import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler; import com.sun.tools.javac.util.Log.WriterKind; import static com.sun.tools.javac.code.Kinds.Kind.*; @@ -484,7 +482,8 @@ public boolean exists() { return false; } }; - + allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) && + Feature.VALUE_CLASSES.allowedInSource(source); } /* Switches: @@ -572,6 +571,9 @@ public boolean exists() { */ private final Symbol silentFail; + /** Switch: are value classes allowed */ + private final boolean allowValueClasses; + protected boolean shouldStop(CompileState cs) { CompileState shouldStopPolicy = (errorCount() > 0 || unrecoverableError()) ? shouldStopPolicyIfError @@ -1546,8 +1548,6 @@ class ScanNested extends TreeScanner { Set> dependencies = new LinkedHashSet<>(); protected boolean hasLambdas; protected boolean hasPatterns; - protected boolean hasValueClasses; - protected boolean hasStrictFields; @Override public void visitClassDef(JCClassDecl node) { Type st = types.supertype(node.sym.type); @@ -1559,7 +1559,6 @@ public void visitClassDef(JCClassDecl node) { if (dependencies.add(stEnv)) { boolean prevHasLambdas = hasLambdas; boolean prevHasPatterns = hasPatterns; - boolean prevHasStrictFields = hasStrictFields; try { scan(stEnv.tree); } finally { @@ -1572,14 +1571,12 @@ public void visitClassDef(JCClassDecl node) { */ hasLambdas = prevHasLambdas; hasPatterns = prevHasPatterns; - hasStrictFields = prevHasStrictFields; } } envForSuperTypeFound = true; } st = types.supertype(st); } - hasValueClasses = node.sym.isValueClass(); super.visitClassDef(node); } @Override @@ -1619,12 +1616,6 @@ public void visitSwitchExpression(JCSwitchExpression tree) { hasPatterns |= tree.patternSwitch; super.visitSwitchExpression(tree); } - - @Override - public void visitVarDef(JCVariableDecl tree) { - hasStrictFields |= tree.sym.isStrict(); - super.visitVarDef(tree); - } } ScanNested scanner = new ScanNested(); scanner.scan(env.tree); @@ -1710,13 +1701,13 @@ public void visitVarDef(JCVariableDecl tree) { compileStates.put(env, CompileState.UNLAMBDA); } - if (scanner.hasValueClasses || scanner.hasStrictFields) { - if (shouldStop(CompileState.STRICT_FIELDS_PROXIES)) + if (allowValueClasses) { + if (shouldStop(CompileState.FIELDS_PROXIES)) return; for (JCTree def : cdefs) { LocalProxyVarsGen.instance(context).translateTopLevelClass(def, localMake); } - compileStates.put(env, CompileState.STRICT_FIELDS_PROXIES); + compileStates.put(env, CompileState.FIELDS_PROXIES); } //generate code for each class diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java new file mode 100644 index 00000000000..77d81fcb3fd --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java @@ -0,0 +1,20 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8359370 + * @summary [lworld] allow instance fields of identity classes to be readable in the prologue phase + * @compile/fail/ref=EarlyAssignments2.out -XDrawDiagnostics EarlyAssignments2.java + * @enablePreview + */ + +public class EarlyAssignments2 { + public static class Inner1 { + public int x; + } + + public Inner1(int y) { + y = x; // FAIL - x might not have been initialized + y = this.x; // FAIL - x might not have been initialized + y = Inner1.this.x; // FAIL - x might not have been initialized + super(); + } +} diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out new file mode 100644 index 00000000000..e69de29bb2d From 78f8d69b57a845eac5f1c52c5542cf72031d2cc2 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 17 Jun 2025 17:59:37 -0400 Subject: [PATCH 03/19] experimental development --- .../com/sun/tools/javac/comp/Attr.java | 69 +++++++++- .../com/sun/tools/javac/comp/AttrContext.java | 6 +- .../com/sun/tools/javac/comp/Flow.java | 82 +++++++++--- .../tools/javac/comp/LocalProxyVarsGen.java | 33 +++++ .../com/sun/tools/javac/comp/Resolve.java | 123 +++++++++++++++--- .../tools/javac/comp/ThisEscapeAnalyzer.java | 3 +- .../com/sun/tools/javac/tree/TreeInfo.java | 15 ++- .../javac/SuperInit/EarlyAssignments.java | 8 +- .../javac/SuperInit/EarlyAssignments.out | 14 +- .../javac/SuperInit/EarlyAssignments2.java | 25 +++- .../javac/SuperInit/EarlyAssignments2.out | 7 + .../tools/javac/SuperInit/SuperInitFails.java | 4 +- .../tools/javac/SuperInit/SuperInitFails.out | 11 +- .../SuperInit/ValueClassSuperInitFails.out | 11 +- .../ValueObjectCompilationTests.java | 5 +- 15 files changed, 328 insertions(+), 88 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 04c52ade2e5..538933fdda7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -124,6 +124,7 @@ public class Attr extends JCTree.Visitor { final ArgumentAttr argumentAttr; final MatchBindingsComputer matchBindingsComputer; final AttrRecover attrRecover; + final LocalProxyVarsGen localProxyVarsGen; public static Attr instance(Context context) { Attr instance = context.get(attrKey); @@ -163,6 +164,7 @@ protected Attr(Context context) { argumentAttr = ArgumentAttr.instance(context); matchBindingsComputer = MatchBindingsComputer.instance(context); attrRecover = AttrRecover.instance(context); + localProxyVarsGen = LocalProxyVarsGen.instance(context); Options options = Options.instance(context); @@ -958,9 +960,9 @@ public void visitClassDef(JCClassDecl tree) { Optional.ofNullable(env.info.attributionMode.isSpeculative ? argumentAttr.withLocalCacheContext() : null); boolean ctorProloguePrev = env.info.ctorPrologue; - boolean isLocalClassPrev = env.info.isLocalClass; + JCClassDecl localClassPrev = env.info.localClass; try { - env.info.isLocalClass = env.enclMethod != null; + env.info.localClass = env.enclMethod != null ? tree : null; // Local and anonymous classes have not been entered yet, so we need to // do it now. if (env.info.scope.owner.kind.matches(KindSelector.VAL_MTH)) { @@ -994,7 +996,7 @@ public void visitClassDef(JCClassDecl tree) { } finally { localCacheContext.ifPresent(LocalCacheContext::leave); env.info.ctorPrologue = ctorProloguePrev; - env.info.isLocalClass = isLocalClassPrev; + env.info.localClass = localClassPrev; } } @@ -4394,6 +4396,28 @@ public void visitIdent(JCIdent tree) { } result = checkId(tree, env1.enclClass.sym.type, sym, env, resultInfo); + + if (env.info.ctorPrologue && allowValueClasses) { + Resolve.FindEnclosingSelect findEnclosingSelect = rs.new FindEnclosingSelect(tree); + findEnclosingSelect.scan(env.tree); + // this is the top select + if (findEnclosingSelect.enclosingSelect == null) { + //System.err.println(sym.owner != env.enclClass.sym); + //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, + // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)); + if (sym.owner != env.enclClass.sym || + TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { + // in this case we are seeing something like `super.field` or accessing + // a field of a super class while in the prologue + Symbol theSym = TreeInfo.symbolFor(tree); + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym)) { + log.error(tree, Errors.CantRefBeforeCtorCalled(theSym)); + } + } else if (localProxyVarsGen.hasAST(env.enclMethod, tree)) { + localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, TreeInfo.symbolFor(tree)); + } + } + } } public void visitSelect(JCFieldAccess tree) { @@ -4530,6 +4554,45 @@ public void visitSelect(JCFieldAccess tree) { env.info.selectSuper = selectSuperPrev; result = checkId(tree, site, sym, env, resultInfo); + if (env.info.ctorPrologue && allowValueClasses) { + Resolve.FindEnclosingSelect findEnclosingSelect = rs.new FindEnclosingSelect(tree); + findEnclosingSelect.scan(env.tree); + // this is the top select + if (findEnclosingSelect.enclosingSelect == null) { + boolean error = false; + //System.err.println(sym.owner != env.enclClass.sym); + //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, + // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)); + if (sym.owner != env.enclClass.sym || + TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, + findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)) { + // in this case we are seeing something like `super.field` or accessing + // a field of a super class while in the prologue + Symbol theSym = TreeInfo.symbolFor(tree); + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym) || + localProxyVarsGen.hasAST(env.enclMethod, tree)) { + error = true; + log.error(tree, Errors.CantRefBeforeCtorCalled(theSym)); + } + } + //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, + // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect.selected : tree)); + if (sym.owner != env.enclClass.sym || + TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, + findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect.selected : tree)) { + // in this case we are seeing something like `super.field` or accessing + // a field of a super class while in the prologue + Symbol theSym = TreeInfo.symbolFor(tree.selected); + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym)) { + error = true; + log.error(tree.selected, Errors.CantRefBeforeCtorCalled(theSym)); + } + } + if (!error && localProxyVarsGen.hasAST(env.enclMethod, tree)) { + localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, TreeInfo.symbolFor(tree)); + } + } + } } //where /** Determine symbol referenced by a Select expression, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java index 16325711d3b..901bb08003d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java @@ -133,9 +133,9 @@ public class AttrContext { boolean instanceInitializerBlock = false; /** - * Is this an attribution environment for a local class + * Local class being attributed if any */ - boolean isLocalClass = false; + JCTree.JCClassDecl localClass = null; /** Duplicate this context, replacing scope field and copying all others. */ @@ -162,7 +162,7 @@ AttrContext dup(WriteableScope scope) { info.allowProtectedAccess = allowProtectedAccess; info.instanceInitializerBlock = instanceInitializerBlock; info.isPermitsClause = isPermitsClause; - info.isLocalClass = isLocalClass; + info.localClass = localClass; return info; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 6a9d2f16d3a..f5165c606b8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -217,6 +217,7 @@ public class Flow { private Env attrEnv; private Lint lint; private final Infer infer; + private final LocalProxyVarsGen localProxyVarsGen; private final UnsetFieldsInfo unsetFieldsInfo; private final boolean allowValueClasses; @@ -345,6 +346,7 @@ protected Flow(Context context) { rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); unsetFieldsInfo = UnsetFieldsInfo.instance(context); + localProxyVarsGen = LocalProxyVarsGen.instance(context); Preview preview = Preview.instance(context); Source source = Source.instance(context); allowValueClasses = (!preview.isPreview(Source.Feature.VALUE_CLASSES) || preview.isEnabled()) && @@ -487,7 +489,13 @@ protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) { // Do something with static or non-static field initializers and initialization blocks. protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer handler) { - forEachInitializer(classDef, isStatic, false, handler); + forEachInitializer(classDef, isStatic, InitializerDisc.PROCESS_ALL, handler); + } + + enum InitializerDisc { + PROCESS_ALL, + EARLY_ONLY, + LATE_ONLY } /* Do something with static or non-static field initializers and initialization blocks. @@ -495,7 +503,7 @@ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consum * initializers we want to process only those before a super() invocation and ignore them after * it. */ - protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, boolean earlyOnly, + protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, InitializerDisc discriminator, Consumer handler) { if (classDef == initScanClass) // avoid infinite loops return; @@ -515,15 +523,19 @@ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, boolea */ boolean isDefStatic = ((TreeInfo.flags(def) | (TreeInfo.symbolFor(def) == null ? 0 : TreeInfo.symbolFor(def).flags_field)) & STATIC) != 0; if (!def.hasTag(METHODDEF) && (isDefStatic == isStatic)) { - if (def instanceof JCVariableDecl varDecl) { - boolean isEarly = varDecl.init != null && - varDecl.sym.isStrict() && - !varDecl.sym.isStatic(); - if (isEarly == earlyOnly) { + if (discriminator == InitializerDisc.PROCESS_ALL) { + handler.accept(def); + } else { + if (def instanceof JCVariableDecl varDecl) { + boolean isEarly = varDecl.init != null && + //varDecl.sym.isStrict() && + !varDecl.sym.isStatic(); + if (isEarly && discriminator == InitializerDisc.EARLY_ONLY) { + handler.accept(def); + } + } else if (discriminator == InitializerDisc.LATE_ONLY) { handler.accept(def); } - } else if (!earlyOnly) { - handler.accept(def); } } } @@ -2206,6 +2218,7 @@ public AssignAnalyzer() { private boolean isConstructor; private boolean isCompactOrGeneratedRecordConstructor; private boolean ctorPrologue; + private JCMethodDecl currentMethod; @Override protected void markDead() { @@ -2228,10 +2241,18 @@ protected boolean trackable(VarSymbol sym) { boolean isFinalOrUninitializedField(VarSymbol sym) { return sym.owner.kind == TYP && (((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL || - (allowValueClasses && (sym.flags() & (HASINIT | PARAMETER)) == 0)) && + (allowValueClasses && (sym.flags() & (HASINIT | PARAMETER)) == 0) && !sym.isStatic()) && classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); } + // is non final instance field + boolean isNonFinalUnitializedField(VarSymbol sym) { + return sym.owner.kind == TYP && + ((!sym.isFinal() && (sym.flags() & (HASINIT | PARAMETER)) == 0 && + !sym.isStatic()) && + classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); + } + /** Initialize new trackable variable by setting its address field * to the next available sequence number and entering it under that * index into the vars array. @@ -2330,10 +2351,10 @@ void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { trackable(sym) && !inits.isMember(sym.adr) && (sym.flags_field & CLASH) == 0) { - if ((!allowValueClasses || (allowValueClasses && ctorPrologue && ((sym.flags() & (HASINIT | PARAMETER))) == 0))) { + //if ((!allowValueClasses || (allowValueClasses && ctorPrologue && ((sym.flags() & (HASINIT | PARAMETER))) == 0))) { log.error(pos, errkey); inits.incl(sym.adr); - } + //} } } @@ -2532,11 +2553,13 @@ public void visitMethodDef(JCMethodDecl tree) { boolean isConstructorPrev = isConstructor; boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor; boolean ctorProloguePrev = ctorPrologue; + JCMethodDecl currentMethodPrev = currentMethod; try { isConstructor = TreeInfo.isConstructor(tree); isCompactOrGeneratedRecordConstructor = isConstructor && ((tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD)); ctorPrologue = isConstructor; + currentMethod = tree; // We only track field initialization inside constructors if (!isConstructor) { @@ -2565,15 +2588,18 @@ public void visitMethodDef(JCMethodDecl tree) { scan(tree.body); if (isConstructor) { - boolean isSynthesized = (tree.sym.flags() & - GENERATEDCONSTR) != 0; + boolean isAGeneratedConstructor = (tree.sym.flags() & GENERATEDCONSTR) != 0; for (int i = firstadr; i < nextadr; i++) { JCVariableDecl vardecl = vardecls[i]; VarSymbol var = vardecl.sym; if (var.owner == classDef.sym && !var.isStatic()) { + // ignore non-final instance fields + if (allowValueClasses && var.owner.kind == TYP && !var.isFinal()) { + continue; + } // choose the diagnostic position based on whether - // the ctor is default(synthesized) or not - if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { + // the ctor is default(generated) or not + if (isAGeneratedConstructor && !isCompactOrGeneratedRecordConstructor) { checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var, Errors.VarNotInitializedInDefaultConstructor(var)); } else if (isCompactOrGeneratedRecordConstructor) { @@ -2609,6 +2635,7 @@ public void visitMethodDef(JCMethodDecl tree) { isConstructor = isConstructorPrev; isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev; ctorPrologue = ctorProloguePrev; + currentMethod = currentMethodPrev; } } finally { lint = lintPrev; @@ -3092,12 +3119,15 @@ public void visitApply(JCMethodInvocation tree) { Name name = TreeInfo.name(tree.meth); // let's process early initializers if (name == names._super) { - forEachInitializer(classDef, false, true, def -> { + forEachInitializer(classDef, false, InitializerDisc.EARLY_ONLY, def -> { scan(def); clearPendingExits(false); }); } scanExpr(tree.meth); + if (name == names._super) { + ctorPrologue = false; + } scanExprs(tree.args); // Handle superclass constructor invocations @@ -3114,11 +3144,10 @@ public void visitApply(JCMethodInvocation tree) { checkInit(TreeInfo.diagEndPos(tree), var, Errors.StrictFieldNotHaveBeenInitializedBeforeSuper(var)); } } - forEachInitializer(classDef, false, def -> { + forEachInitializer(classDef, false, InitializerDisc.LATE_ONLY, def -> { scan(def); clearPendingExits(false); }); - ctorPrologue = false; } // If this(): at this point all final uninitialized fields will get initialized @@ -3204,13 +3233,24 @@ public void visitAssign(JCAssign tree) { // check fields accessed through this. are definitely // assigned before reading their value - public void visitSelect(JCFieldAccess tree) { + /*public void visitSelect(JCFieldAccess tree) { super.visitSelect(tree); if ((TreeInfo.isThisQualifier(tree.selected) || - (classDef != null && TreeInfo.isExplicitThisReference(types, (Type.ClassType)classDef.type, tree.selected))) && + (classDef != null && TreeInfo.isExplicitThisOrSuperReference(types, (Type.ClassType)classDef.type, tree.selected))) && tree.sym.kind == VAR) { checkInit(tree.pos(), (VarSymbol)tree.sym); } + }*/ + public void visitSelect(JCFieldAccess tree) { + super.visitSelect(tree); + if (TreeInfo.isThisQualifier(tree.selected) && tree.sym.kind == VAR) { + if (trackable((VarSymbol)tree.sym) && !isNonFinalUnitializedField((VarSymbol)tree.sym)) { + checkInit(tree.pos(), (VarSymbol) tree.sym); + } + } + if (localProxyVarsGen.hasAST(currentMethod, tree)) { + checkInit(tree.pos(), (VarSymbol)tree.sym); + } } public void visitAssignop(JCAssignOp tree) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java index e1465b8bce0..dee5ed2aceb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -90,6 +91,7 @@ public static LocalProxyVarsGen instance(Context context) { private ClassSymbol currentClass = null; private java.util.List instanceFields; private Map> fieldsReadInPrologue = new HashMap<>(); + private Map> ASTsReadInPrologue = new HashMap<>(); private final boolean noLocalProxyVars; @@ -111,6 +113,37 @@ public void addFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) { fieldsReadInPrologue.put(constructor, fieldSet); } + public boolean removeSymReadInPrologue(JCMethodDecl constructor, Symbol sym) { + Set fieldSet = fieldsReadInPrologue.get(constructor); + if (fieldSet != null) { + return fieldSet.remove(sym); + } + return false; + } + + public void addASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { + // better to have order for this one + Set treeSet = ASTsReadInPrologue.getOrDefault(constructor, new LinkedHashSet<>()); + treeSet.add(tree); + ASTsReadInPrologue.put(constructor, treeSet); + } + + public boolean removeASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { + Set treeSet = ASTsReadInPrologue.get(constructor); + if (treeSet != null) { + return treeSet.remove(tree); + } + return false; + } + + public boolean hasAST(JCMethodDecl constructor, JCTree tree) { + Set treeSet = ASTsReadInPrologue.get(constructor); + if (treeSet != null) { + return treeSet.contains(tree); + } + return false; + } + public JCTree translateTopLevelClass(JCTree cdef, TreeMaker make) { if (!noLocalProxyVars) { try { 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 96618b4a574..c4c121c2352 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 @@ -1540,12 +1540,34 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { (sym.flags() & STATIC) == 0) { if (staticOnly) return new StaticError(sym); - if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbol)sym)) { - if (!sym.isStrictInstance()) { - return new RefBeforeCtorCalledError(sym); - } else { - localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, sym); - return sym; + if (env1.info.ctorPrologue) { + EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym, true); + switch (erk) { + case NOT_IN_LHS_OF_ASSIGN: + if (allowValueClasses) { + /* at this point we don't have enough info, we are seeing a sub tree which could + * be part of a select or something bigger. Problem is that we will probably need + * to come back and remove the symbol from the corresponding data structure in + * LocalProxyVarsGen + */ + JCTree tree = pos.getTree(); + FindEnclosingSelect findEnclosingSelect = new FindEnclosingSelect(tree); + findEnclosingSelect.scan(env1.tree); + tree = findEnclosingSelect.enclosingSelect == null ? tree : findEnclosingSelect.enclosingSelect; + if (tree == pos.getTree()) { + localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); + } else { + localProxyVarsGen.addASTReadInPrologue(env1.enclMethod, tree); + } + return sym; + } else { + return new RefBeforeCtorCalledError(sym); + } + case INSIDE_LAMBDA_OR_LOCAL_CLASS: + case NOT_ACCEPTABLE_OTHER: + return new RefBeforeCtorCalledError(sym); + default: + return sym; } } } @@ -3888,8 +3910,31 @@ Symbol resolveSelf(DiagnosticPosition pos, if (sym != null) { if (staticOnly) sym = new StaticError(sym); - else if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbol)sym)) - sym = new RefBeforeCtorCalledError(sym); + else if (env1.info.ctorPrologue) { + EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym, true); + switch (erk) { + case NOT_IN_LHS_OF_ASSIGN: + if (allowValueClasses) { + JCTree tree = pos.getTree(); + FindEnclosingSelect findEnclosingSelect = new FindEnclosingSelect(tree); + findEnclosingSelect.scan(env1.tree); + tree = findEnclosingSelect.enclosingSelect == null ? tree : findEnclosingSelect.enclosingSelect; + if (tree == pos.getTree()) { + localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); + } else { + localProxyVarsGen.addASTReadInPrologue(env1.enclMethod, tree); + } + } else { + sym = new RefBeforeCtorCalledError(sym); + } + break; + case INSIDE_LAMBDA_OR_LOCAL_CLASS: + case NOT_ACCEPTABLE_OTHER: + sym = new RefBeforeCtorCalledError(sym); + break; + default: // do nothing + } + } return accessBase(sym, pos, env.enclClass.sym.type, name, true); } @@ -3925,6 +3970,26 @@ else if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbo return syms.errSymbol; } //where + public class FindEnclosingSelect extends TreeScanner { + JCTree treeToLookFor; + JCFieldAccess enclosingSelect = null; + + FindEnclosingSelect(JCTree treeToLookFor) { + this.treeToLookFor = treeToLookFor; + } + + @Override + public void visitSelect(JCFieldAccess tree) { + if (tree.selected == treeToLookFor) { + enclosingSelect = tree; + // this select could be part of an enclosing select + treeToLookFor = tree; + } else { + scan(tree.selected); + } + } + } + private List pruneInterfaces(Type t) { ListBuffer result = new ListBuffer<>(); for (Type t1 : types.interfaces(t)) { @@ -3958,18 +4023,31 @@ private List pruneInterfaces(Type t) { * To catch those cases, we rely on similar logic in Attr.checkAssignable(). */ private boolean isAllowedEarlyReference(DiagnosticPosition pos, Env env, VarSymbol v) { + return isAllowedEarlyReference(pos, env, v, false) == EarlyReferenceKind.ACCEPTABLE; + } + private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env env, VarSymbol v, boolean ignored) { // Check assumptions Assert.check(env.info.ctorPrologue); Assert.check((v.flags_field & STATIC) == 0); + // The assignment statement must not be within a lambda or a local class + if (env.info.isLambda) { + return EarlyReferenceKind.INSIDE_LAMBDA_OR_LOCAL_CLASS; + } + if (env.info.localClass != null) { + Symbol sym = env.info.localClass.sym; + for (; sym != null && sym.kind != PCK; sym = sym.owner.kind == TYP ? sym.owner : null) { + if (sym == v.owner) break; + } + if (sym == null) { + return EarlyReferenceKind.INSIDE_LAMBDA_OR_LOCAL_CLASS; + } + } + // The symbol must appear in the LHS of an assignment statement if (!(env.tree instanceof JCAssign assign)) - return false; - - // The assignment statement must not be within a lambda - if (env.info.isLambda) - return false; + return EarlyReferenceKind.NOT_IN_LHS_OF_ASSIGN; // Get the symbol's qualifier, if any JCExpression lhs = TreeInfo.skipParens(assign.lhs); @@ -3981,23 +4059,30 @@ private boolean isAllowedEarlyReference(DiagnosticPosition pos, Env case SELECT: JCFieldAccess select = (JCFieldAccess)lhs; base = select.selected; - if (!TreeInfo.isExplicitThisReference(types, (ClassType)env.enclClass.type, base)) - return false; + if (!TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, base)) + return EarlyReferenceKind.NOT_ACCEPTABLE_OTHER; break; default: - return false; + return EarlyReferenceKind.NOT_ACCEPTABLE_OTHER; } // If an early reference, the field must not be declared in a superclass if (isEarlyReference(env, base, v) && v.owner != env.enclClass.sym) - return false; + return EarlyReferenceKind.NOT_ACCEPTABLE_OTHER; // The flexible constructors feature must be enabled preview.checkSourceLevel(pos, Feature.FLEXIBLE_CONSTRUCTORS); // OK - return true; + return EarlyReferenceKind.ACCEPTABLE; } + // where + enum EarlyReferenceKind { + NOT_IN_LHS_OF_ASSIGN, // this is a not acceptable state for the method above + INSIDE_LAMBDA_OR_LOCAL_CLASS, // this is a not acceptable state for the method above + ACCEPTABLE, + NOT_ACCEPTABLE_OTHER // this is a not acceptable state for the method above + } /** * Determine if the variable appearance constitutes an early reference to the current class. @@ -4019,7 +4104,7 @@ public boolean isEarlyReference(Env env, JCTree base, VarSymbol v) (v.flags() & STATIC) == 0 && v.owner.kind == TYP && types.isSubtype(env.enclClass.type, v.owner.type) && - (base == null || TreeInfo.isExplicitThisReference(types, (ClassType)env.enclClass.type, base)); + (base == null || TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, base)); } /* *************************************************************************** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 77ddd5c6583..bf239954f93 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -52,7 +52,6 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; -import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.TreeInfo; @@ -975,7 +974,7 @@ public void visitSelect(JCFieldAccess tree) { // Explicit 'this' reference? The expression references whatever 'this' references Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type; - if (TreeInfo.isExplicitThisReference(types, currentClassType, tree)) { + if (TreeInfo.isExplicitThisOrSuperReference(types, currentClassType, tree)) { refs.find(ThisRef.class) .map(ref -> new ExprRef(depth, ref)) .forEach(refs::add); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 5e3b043fb11..e06e7c544b7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -31,7 +31,6 @@ import com.sun.source.util.TreePath; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.RecordComponent; -import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*; @@ -189,14 +188,20 @@ public static boolean isIdentOrThisDotIdent(JCTree tree) { * - A 'this' identifier qualified by a class name whose type is 'currentClass' or a supertype * but also NOT an enclosing outer class of 'currentClass'. */ - public static boolean isExplicitThisReference(Types types, Type.ClassType currentClass, JCTree tree) { + public static boolean isExplicitThisOrSuperReference(Types types, Type.ClassType currentClass, JCTree tree) { + return isExplicitThisOrSuperReference(types, currentClass, tree, false); + } + + public static boolean isExplicitThisOrSuperReference(Types types, Type.ClassType currentClass, JCTree tree, boolean superReferenceOnly) { switch (tree.getTag()) { case PARENS: - return isExplicitThisReference(types, currentClass, skipParens(tree)); + return isExplicitThisOrSuperReference(types, currentClass, skipParens(tree)); case IDENT: { JCIdent ident = (JCIdent)tree; Names names = ident.name.table.names; - return ident.name == names._this || ident.name == names._super; + return /*superReferenceOnly ? + ident.name == names._super :*/ + ident.name == names._this || ident.name == names._super; } case SELECT: { JCFieldAccess select = (JCFieldAccess)tree; @@ -208,7 +213,7 @@ public static boolean isExplicitThisReference(Types types, Type.ClassType curren Names names = select.name.table.names; return currentClassSym.isSubClass(selectedClassSym, types) && (select.name == names._super || - (select.name == names._this && + (!superReferenceOnly && select.name == names._this && (currentClassSym == selectedClassSym || !currentClassSym.isEnclosedBy(selectedClassSym)))); } diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java index c3cad5d7016..641ff767a01 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java @@ -17,12 +17,12 @@ public Inner1() { super(); } - public Inner1(int y) { + /*public Inner1(int y) { y = x; // FAIL - early 'this' reference y = this.x; // FAIL - early 'this' reference y = Inner1.this.x; // FAIL - early 'this' reference super(); - } + }*/ public class Inner1a extends Inner1 { public int z; @@ -95,13 +95,13 @@ public static class Inner4 { public Inner4() { x = 0; // OK - x = x + 1; // FAIL - illegal early access + x = x + 1; // OK super(); } public Inner4(int a) { this.x = 0; // OK - this.x = this.x + 1; // FAIL - illegal early access + this.x = this.x + 1; // OK super(); } diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out index 38182c2d312..872609e9585 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out @@ -1,7 +1,4 @@ -EarlyAssignments.java:21:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:22:17: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:23:23: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:31:21: compiler.err.cant.ref.before.ctor.called: super +EarlyAssignments.java:31:26: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:32:21: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:33:26: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:34:34: compiler.err.cant.ref.before.ctor.called: x @@ -13,17 +10,14 @@ EarlyAssignments.java:66:13: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:67:17: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:68:25: compiler.err.cant.ref.before.ctor.called: this EarlyAssignments.java:69:31: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:98:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:104:22: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:110:35: compiler.err.cant.ref.before.ctor.called: this EarlyAssignments.java:119:17: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:124:22: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:129:29: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:134:17: compiler.err.cant.ref.before.ctor.called: super -EarlyAssignments.java:139:23: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:134:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:139:28: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:148:13: compiler.err.cant.assign.initialized.before.ctor.called: x EarlyAssignments.java:157:13: compiler.err.cant.assign.val.to.var: final, x EarlyAssignments.java:168:13: compiler.err.cant.ref.before.ctor.called: this - compiler.note.preview.filename: EarlyAssignments.java, DEFAULT - compiler.note.preview.recompile -26 errors +20 errors diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java index 77d81fcb3fd..43751734657 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java @@ -9,12 +9,25 @@ public class EarlyAssignments2 { public static class Inner1 { public int x; - } - public Inner1(int y) { - y = x; // FAIL - x might not have been initialized - y = this.x; // FAIL - x might not have been initialized - y = Inner1.this.x; // FAIL - x might not have been initialized - super(); + public Inner1(int y) { + y = x; // FAIL - x might not have been initialized + super(); + } + + public Inner1(int y, int ignore1) { + y = this.x; // FAIL - x might not have been initialized + super(); + } + + public Inner1(int y, boolean ignore2) { + y = Inner1.this.x; // FAIL - x might not have been initialized + super(); + } + + public Inner1(short[] x) { + this.x++; // this should FAIL + super(); + } } } diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out index e69de29bb2d..257aa3e4909 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out @@ -0,0 +1,7 @@ +EarlyAssignments2.java:14:17: compiler.err.var.might.not.have.been.initialized: x +EarlyAssignments2.java:19:21: compiler.err.var.might.not.have.been.initialized: x +EarlyAssignments2.java:24:28: compiler.err.var.might.not.have.been.initialized: x +EarlyAssignments2.java:29:17: compiler.err.var.might.not.have.been.initialized: x +- compiler.note.preview.filename: EarlyAssignments2.java, DEFAULT +- compiler.note.preview.recompile +4 errors diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.java b/test/langtools/tools/javac/SuperInit/SuperInitFails.java index feb5b81c1b0..34a716adbb8 100644 --- a/test/langtools/tools/javac/SuperInit/SuperInitFails.java +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.java @@ -89,12 +89,12 @@ public SuperInitFails(char[] x) { return; // this should FAIL super(); } - +/* public SuperInitFails(short[] x) { this.x++; // this should FAIL super(); } - +*/ public SuperInitFails(float[] x) { System.identityHashCode(this); // this should FAIL super(); diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out index a6cabb6f5ed..bdf4d55251a 100644 --- a/test/langtools/tools/javac/SuperInit/SuperInitFails.out +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out @@ -1,9 +1,8 @@ SuperInitFails.java:57:9: compiler.err.cant.ref.before.ctor.called: hashCode() -SuperInitFails.java:62:9: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: super -SuperInitFails.java:72:23: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:77:23: compiler.err.cant.ref.before.ctor.called: super -SuperInitFails.java:94:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:62:13: compiler.err.cant.ref.before.ctor.called: hashCode() +SuperInitFails.java:67:14: compiler.err.cant.ref.before.ctor.called: hashCode() +SuperInitFails.java:72:28: compiler.err.cant.ref.before.ctor.called: hashCode() +SuperInitFails.java:77:29: compiler.err.cant.ref.before.ctor.called: hashCode() SuperInitFails.java:99:33: compiler.err.cant.ref.before.ctor.called: this SuperInitFails.java:104:14: compiler.err.cant.ref.before.ctor.called: this SuperInitFails.java:108:20: compiler.err.not.encl.class: java.lang.Object @@ -33,4 +32,4 @@ SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized SuperInitFails.java:150:18: compiler.err.call.must.only.appear.in.ctor - compiler.note.preview.filename: SuperInitFails.java, DEFAULT - compiler.note.preview.recompile -33 errors +32 errors diff --git a/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out index 914a97b63aa..062c123e118 100644 --- a/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out +++ b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out @@ -1,8 +1,8 @@ ValueClassSuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: hashCode() -ValueClassSuperInitFails.java:72:9: compiler.err.cant.ref.before.ctor.called: this -ValueClassSuperInitFails.java:77:9: compiler.err.cant.ref.before.ctor.called: super -ValueClassSuperInitFails.java:82:33: compiler.err.cant.ref.before.ctor.called: this -ValueClassSuperInitFails.java:87:33: compiler.err.cant.ref.before.ctor.called: super +ValueClassSuperInitFails.java:72:13: compiler.err.cant.ref.before.ctor.called: hashCode() +ValueClassSuperInitFails.java:77:14: compiler.err.cant.ref.before.ctor.called: hashCode() +ValueClassSuperInitFails.java:82:38: compiler.err.cant.ref.before.ctor.called: hashCode() +ValueClassSuperInitFails.java:87:39: compiler.err.cant.ref.before.ctor.called: hashCode() ValueClassSuperInitFails.java:109:33: compiler.err.cant.ref.before.ctor.called: this ValueClassSuperInitFails.java:114:14: compiler.err.cant.ref.before.ctor.called: this ValueClassSuperInitFails.java:118:20: compiler.err.not.encl.class: java.lang.Object @@ -12,6 +12,7 @@ ValueClassSuperInitFails.java:135:9: compiler.err.cant.ref.before.ctor.called: t ValueClassSuperInitFails.java:143:9: compiler.err.non.canonical.constructor.invoke.another.constructor: ValueClassSuperInitFails.Record1 ValueClassSuperInitFails.java:148:9: compiler.err.non.canonical.constructor.invoke.another.constructor: ValueClassSuperInitFails.Record2 ValueClassSuperInitFails.java:161:41: compiler.err.cant.ref.before.ctor.called: this +ValueClassSuperInitFails.java:175:49: compiler.err.cant.ref.before.ctor.called: x ValueClassSuperInitFails.java:179:15: compiler.err.cant.ref.before.ctor.called: this ValueClassSuperInitFails.java:43:13: compiler.err.call.must.only.appear.in.ctor ValueClassSuperInitFails.java:47:14: compiler.err.call.must.only.appear.in.ctor @@ -24,4 +25,4 @@ ValueClassSuperInitFails.java:99:13: compiler.err.return.before.superclass.initi ValueClassSuperInitFails.java:170:18: compiler.err.call.must.only.appear.in.ctor - compiler.note.preview.filename: ValueClassSuperInitFails.java, DEFAULT - compiler.note.preview.recompile -24 errors +25 errors diff --git a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java index 1b8a03f5494..d3f9ccc2b22 100644 --- a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java +++ b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java @@ -919,11 +919,12 @@ value class V { } """ ); - assertOK( + assertFail("compiler.err.cant.ref.before.ctor.called", """ value class Test { Test t = null; - Runnable r = () -> { System.err.println(t); }; // compiler will generate a local proxy for `t` + // can't reference instance field from a lambda before super constructor has been invoked + Runnable r = () -> { System.err.println(t); }; } """ ); From a5a6a35f864db5154853b00b371a08dbfc7871c0 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 20 Jun 2025 17:23:44 -0400 Subject: [PATCH 04/19] cleanup --- .../com/sun/tools/javac/comp/Attr.java | 37 ++++--------------- .../com/sun/tools/javac/tree/TreeInfo.java | 10 +---- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 538933fdda7..c2890856b0e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4402,19 +4402,16 @@ public void visitIdent(JCIdent tree) { findEnclosingSelect.scan(env.tree); // this is the top select if (findEnclosingSelect.enclosingSelect == null) { - //System.err.println(sym.owner != env.enclClass.sym); - //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, - // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)); if (sym.owner != env.enclClass.sym || TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { // in this case we are seeing something like `super.field` or accessing // a field of a super class while in the prologue - Symbol theSym = TreeInfo.symbolFor(tree); - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym)) { - log.error(tree, Errors.CantRefBeforeCtorCalled(theSym)); + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, tree.sym)) { + log.error(tree, Errors.CantRefBeforeCtorCalled(tree.sym)); } } else if (localProxyVarsGen.hasAST(env.enclMethod, tree)) { - localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, TreeInfo.symbolFor(tree)); + localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, tree.sym); + throw new AssertionError("assertion at visitIdent"); } } } @@ -4560,36 +4557,18 @@ public void visitSelect(JCFieldAccess tree) { // this is the top select if (findEnclosingSelect.enclosingSelect == null) { boolean error = false; - //System.err.println(sym.owner != env.enclClass.sym); - //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, - // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)); if (sym.owner != env.enclClass.sym || - TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, - findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect : tree)) { + TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { // in this case we are seeing something like `super.field` or accessing // a field of a super class while in the prologue - Symbol theSym = TreeInfo.symbolFor(tree); - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym) || + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, tree.sym) || localProxyVarsGen.hasAST(env.enclMethod, tree)) { + log.error(tree, Errors.CantRefBeforeCtorCalled(tree.sym)); error = true; - log.error(tree, Errors.CantRefBeforeCtorCalled(theSym)); - } - } - //System.err.println(TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, - // findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect.selected : tree)); - if (sym.owner != env.enclClass.sym || - TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, - findEnclosingSelect.enclosingSelect != null ? findEnclosingSelect.enclosingSelect.selected : tree)) { - // in this case we are seeing something like `super.field` or accessing - // a field of a super class while in the prologue - Symbol theSym = TreeInfo.symbolFor(tree.selected); - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, theSym)) { - error = true; - log.error(tree.selected, Errors.CantRefBeforeCtorCalled(theSym)); } } if (!error && localProxyVarsGen.hasAST(env.enclMethod, tree)) { - localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, TreeInfo.symbolFor(tree)); + localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, tree.sym); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index e06e7c544b7..fc963a5d962 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -189,19 +189,13 @@ public static boolean isIdentOrThisDotIdent(JCTree tree) { * but also NOT an enclosing outer class of 'currentClass'. */ public static boolean isExplicitThisOrSuperReference(Types types, Type.ClassType currentClass, JCTree tree) { - return isExplicitThisOrSuperReference(types, currentClass, tree, false); - } - - public static boolean isExplicitThisOrSuperReference(Types types, Type.ClassType currentClass, JCTree tree, boolean superReferenceOnly) { switch (tree.getTag()) { case PARENS: return isExplicitThisOrSuperReference(types, currentClass, skipParens(tree)); case IDENT: { JCIdent ident = (JCIdent)tree; Names names = ident.name.table.names; - return /*superReferenceOnly ? - ident.name == names._super :*/ - ident.name == names._this || ident.name == names._super; + return ident.name == names._this || ident.name == names._super; } case SELECT: { JCFieldAccess select = (JCFieldAccess)tree; @@ -213,7 +207,7 @@ public static boolean isExplicitThisOrSuperReference(Types types, Type.ClassType Names names = select.name.table.names; return currentClassSym.isSubClass(selectedClassSym, types) && (select.name == names._super || - (!superReferenceOnly && select.name == names._this && + (select.name == names._this && (currentClassSym == selectedClassSym || !currentClassSym.isEnclosedBy(selectedClassSym)))); } From 23ec9e451051058363658aeb647f733e800ce59d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 20 Jun 2025 18:53:15 -0400 Subject: [PATCH 05/19] additional cleanup --- .../com/sun/tools/javac/comp/Attr.java | 8 +--- .../com/sun/tools/javac/comp/Flow.java | 17 -------- .../javac/SuperInit/EarlyAssignments.java | 7 ---- .../javac/SuperInit/EarlyAssignments.out | 40 +++++++++---------- .../tools/javac/SuperInit/SuperInitFails.java | 7 +--- .../tools/javac/SuperInit/SuperInitFails.out | 38 +++++++++--------- 6 files changed, 42 insertions(+), 75 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index c2890856b0e..731809b311a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4400,18 +4400,14 @@ public void visitIdent(JCIdent tree) { if (env.info.ctorPrologue && allowValueClasses) { Resolve.FindEnclosingSelect findEnclosingSelect = rs.new FindEnclosingSelect(tree); findEnclosingSelect.scan(env.tree); - // this is the top select + // this identifier is standalone not part of a select if (findEnclosingSelect.enclosingSelect == null) { if (sym.owner != env.enclClass.sym || TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { - // in this case we are seeing something like `super.field` or accessing - // a field of a super class while in the prologue + // in this case we are seeing something like and access to `this`, as an identifier, in the prologue if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, tree.sym)) { log.error(tree, Errors.CantRefBeforeCtorCalled(tree.sym)); } - } else if (localProxyVarsGen.hasAST(env.enclMethod, tree)) { - localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, tree.sym); - throw new AssertionError("assertion at visitIdent"); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index f5165c606b8..b259003213f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -2217,7 +2217,6 @@ public AssignAnalyzer() { private boolean isConstructor; private boolean isCompactOrGeneratedRecordConstructor; - private boolean ctorPrologue; private JCMethodDecl currentMethod; @Override @@ -2351,10 +2350,8 @@ void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { trackable(sym) && !inits.isMember(sym.adr) && (sym.flags_field & CLASH) == 0) { - //if ((!allowValueClasses || (allowValueClasses && ctorPrologue && ((sym.flags() & (HASINIT | PARAMETER))) == 0))) { log.error(pos, errkey); inits.incl(sym.adr); - //} } } @@ -2552,13 +2549,11 @@ public void visitMethodDef(JCMethodDecl tree) { Assert.check(pendingExits.isEmpty()); boolean isConstructorPrev = isConstructor; boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor; - boolean ctorProloguePrev = ctorPrologue; JCMethodDecl currentMethodPrev = currentMethod; try { isConstructor = TreeInfo.isConstructor(tree); isCompactOrGeneratedRecordConstructor = isConstructor && ((tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD)); - ctorPrologue = isConstructor; currentMethod = tree; // We only track field initialization inside constructors @@ -2634,7 +2629,6 @@ public void visitMethodDef(JCMethodDecl tree) { returnadr = returnadrPrev; isConstructor = isConstructorPrev; isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev; - ctorPrologue = ctorProloguePrev; currentMethod = currentMethodPrev; } } finally { @@ -3125,9 +3119,6 @@ public void visitApply(JCMethodInvocation tree) { }); } scanExpr(tree.meth); - if (name == names._super) { - ctorPrologue = false; - } scanExprs(tree.args); // Handle superclass constructor invocations @@ -3233,14 +3224,6 @@ public void visitAssign(JCAssign tree) { // check fields accessed through this. are definitely // assigned before reading their value - /*public void visitSelect(JCFieldAccess tree) { - super.visitSelect(tree); - if ((TreeInfo.isThisQualifier(tree.selected) || - (classDef != null && TreeInfo.isExplicitThisOrSuperReference(types, (Type.ClassType)classDef.type, tree.selected))) && - tree.sym.kind == VAR) { - checkInit(tree.pos(), (VarSymbol)tree.sym); - } - }*/ public void visitSelect(JCFieldAccess tree) { super.visitSelect(tree); if (TreeInfo.isThisQualifier(tree.selected) && tree.sym.kind == VAR) { diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java index 641ff767a01..0d7c65a7c21 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java @@ -17,13 +17,6 @@ public Inner1() { super(); } - /*public Inner1(int y) { - y = x; // FAIL - early 'this' reference - y = this.x; // FAIL - early 'this' reference - y = Inner1.this.x; // FAIL - early 'this' reference - super(); - }*/ - public class Inner1a extends Inner1 { public int z; public Inner1a(byte value) { diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out index 872609e9585..26780b1e3ab 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out @@ -1,23 +1,23 @@ -EarlyAssignments.java:31:26: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:32:21: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:33:26: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:34:34: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:36:36: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:40:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:44:21: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:48:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:66:13: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:67:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:68:25: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:69:31: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:119:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:124:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:129:29: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:134:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:139:28: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:148:13: compiler.err.cant.assign.initialized.before.ctor.called: x -EarlyAssignments.java:157:13: compiler.err.cant.assign.val.to.var: final, x -EarlyAssignments.java:168:13: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:24:26: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:25:21: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:26:26: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:27:34: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:29:36: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:33:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:37:21: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:41:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:59:13: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:60:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:61:25: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:62:31: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:112:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:117:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:122:29: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:127:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:132:28: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:141:13: compiler.err.cant.assign.initialized.before.ctor.called: x +EarlyAssignments.java:150:13: compiler.err.cant.assign.val.to.var: final, x +EarlyAssignments.java:161:13: compiler.err.cant.ref.before.ctor.called: this - compiler.note.preview.filename: EarlyAssignments.java, DEFAULT - compiler.note.preview.recompile 20 errors diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.java b/test/langtools/tools/javac/SuperInit/SuperInitFails.java index 34a716adbb8..70d53a3cf15 100644 --- a/test/langtools/tools/javac/SuperInit/SuperInitFails.java +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.java @@ -89,12 +89,7 @@ public SuperInitFails(char[] x) { return; // this should FAIL super(); } -/* - public SuperInitFails(short[] x) { - this.x++; // this should FAIL - super(); - } -*/ + public SuperInitFails(float[] x) { System.identityHashCode(this); // this should FAIL super(); diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out index bdf4d55251a..63cb7610d9f 100644 --- a/test/langtools/tools/javac/SuperInit/SuperInitFails.out +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out @@ -3,24 +3,24 @@ SuperInitFails.java:62:13: compiler.err.cant.ref.before.ctor.called: hashCode() SuperInitFails.java:67:14: compiler.err.cant.ref.before.ctor.called: hashCode() SuperInitFails.java:72:28: compiler.err.cant.ref.before.ctor.called: hashCode() SuperInitFails.java:77:29: compiler.err.cant.ref.before.ctor.called: hashCode() -SuperInitFails.java:99:33: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:104:14: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:108:20: compiler.err.not.encl.class: java.lang.Object -SuperInitFails.java:112:17: compiler.err.cant.ref.before.ctor.called: super -SuperInitFails.java:119:22: compiler.err.call.must.only.appear.in.ctor -SuperInitFails.java:125:9: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:133:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record1 -SuperInitFails.java:138:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record2 -SuperInitFails.java:155:31: compiler.err.cant.ref.before.ctor.called: x -SuperInitFails.java:159:15: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:168:13: compiler.err.cant.ref.before.ctor.called: x -SuperInitFails.java:172:17: compiler.err.cant.ref.before.ctor.called: x -SuperInitFails.java:176:24: compiler.err.cant.ref.before.ctor.called: x -SuperInitFails.java:180:18: compiler.err.cant.ref.before.ctor.called: x -SuperInitFails.java:186:28: compiler.err.cant.ref.before.ctor.called: this -SuperInitFails.java:195:25: compiler.err.return.before.superclass.initialized -SuperInitFails.java:200:33: compiler.err.ctor.calls.not.allowed.here -SuperInitFails.java:205:29: compiler.err.redundant.superclass.init +SuperInitFails.java:94:33: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:99:14: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:103:20: compiler.err.not.encl.class: java.lang.Object +SuperInitFails.java:107:17: compiler.err.cant.ref.before.ctor.called: super +SuperInitFails.java:114:22: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:120:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:128:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record1 +SuperInitFails.java:133:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record2 +SuperInitFails.java:150:31: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:154:15: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:163:13: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:167:17: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:171:24: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:175:18: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:181:28: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:190:25: compiler.err.return.before.superclass.initialized +SuperInitFails.java:195:33: compiler.err.ctor.calls.not.allowed.here +SuperInitFails.java:200:29: compiler.err.redundant.superclass.init SuperInitFails.java:33:13: compiler.err.call.must.only.appear.in.ctor SuperInitFails.java:37:14: compiler.err.call.must.only.appear.in.ctor SuperInitFails.java:41:14: compiler.err.call.must.only.appear.in.ctor @@ -29,7 +29,7 @@ SuperInitFails.java:49:33: compiler.err.call.must.only.appear.in.ctor SuperInitFails.java:53:32: compiler.err.call.must.only.appear.in.ctor SuperInitFails.java:83:18: compiler.err.ctor.calls.not.allowed.here SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized -SuperInitFails.java:150:18: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:145:18: compiler.err.call.must.only.appear.in.ctor - compiler.note.preview.filename: SuperInitFails.java, DEFAULT - compiler.note.preview.recompile 32 errors From 4ca62d8391811afbfb694ff09de90cdca66b2a89 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 20 Jun 2025 20:33:09 -0400 Subject: [PATCH 06/19] additional cleanups --- .../com/sun/tools/javac/comp/Resolve.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) 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 c4c121c2352..36afd7745d1 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 @@ -1541,9 +1541,9 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { if (staticOnly) return new StaticError(sym); if (env1.info.ctorPrologue) { - EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym, true); + EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym); switch (erk) { - case NOT_IN_LHS_OF_ASSIGN: + case UNDEFINED: if (allowValueClasses) { /* at this point we don't have enough info, we are seeing a sub tree which could * be part of a select or something bigger. Problem is that we will probably need @@ -3849,7 +3849,7 @@ Symbol findSelfContaining(DiagnosticPosition pos, if (staticOnly) { // current class is not an inner class, stop search return new StaticError(sym); - } else if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbol)sym)) { + } else if (env1.info.ctorPrologue && isAllowedEarlyReference(pos, env1, (VarSymbol)sym) != EarlyReferenceKind.ACCEPTABLE) { // early construction context, stop search return new RefBeforeCtorCalledError(sym); } else { @@ -3911,9 +3911,9 @@ Symbol resolveSelf(DiagnosticPosition pos, if (staticOnly) sym = new StaticError(sym); else if (env1.info.ctorPrologue) { - EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym, true); + EarlyReferenceKind erk = isAllowedEarlyReference(pos, env1, (VarSymbol)sym); switch (erk) { - case NOT_IN_LHS_OF_ASSIGN: + case UNDEFINED: if (allowValueClasses) { JCTree tree = pos.getTree(); FindEnclosingSelect findEnclosingSelect = new FindEnclosingSelect(tree); @@ -4022,11 +4022,7 @@ private List pruneInterfaces(Type t) { * We also don't verify that the field has no initializer, which is required. * To catch those cases, we rely on similar logic in Attr.checkAssignable(). */ - private boolean isAllowedEarlyReference(DiagnosticPosition pos, Env env, VarSymbol v) { - return isAllowedEarlyReference(pos, env, v, false) == EarlyReferenceKind.ACCEPTABLE; - } - - private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env env, VarSymbol v, boolean ignored) { + private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env env, VarSymbol v) { // Check assumptions Assert.check(env.info.ctorPrologue); Assert.check((v.flags_field & STATIC) == 0); @@ -4047,7 +4043,7 @@ private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env Date: Fri, 20 Jun 2025 21:53:35 -0400 Subject: [PATCH 07/19] adding some documentation --- .../share/classes/com/sun/tools/javac/comp/Attr.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 731809b311a..010deacdc36 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -319,7 +319,7 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env Date: Fri, 20 Jun 2025 22:28:37 -0400 Subject: [PATCH 08/19] more cleanups --- .../share/classes/com/sun/tools/javac/comp/Flow.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index b259003213f..8bb726a1ce8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -499,7 +499,7 @@ enum InitializerDisc { } /* Do something with static or non-static field initializers and initialization blocks. - * the `earlyOnly` argument will determine if we will deal or not with early variable instance + * the `discriminator` argument will determine if we will deal or not with early variable instance * initializers we want to process only those before a super() invocation and ignore them after * it. */ @@ -528,7 +528,6 @@ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Initia } else { if (def instanceof JCVariableDecl varDecl) { boolean isEarly = varDecl.init != null && - //varDecl.sym.isStrict() && !varDecl.sym.isStatic(); if (isEarly && discriminator == InitializerDisc.EARLY_ONLY) { handler.accept(def); @@ -2350,8 +2349,8 @@ void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { trackable(sym) && !inits.isMember(sym.adr) && (sym.flags_field & CLASH) == 0) { - log.error(pos, errkey); - inits.incl(sym.adr); + log.error(pos, errkey); + inits.incl(sym.adr); } } From e29bb7845a86c54e52b8182d42f445d00134137d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Sat, 21 Jun 2025 18:32:00 -0400 Subject: [PATCH 09/19] more refactorings --- .../com/sun/tools/javac/comp/Attr.java | 52 +++++++------------ .../com/sun/tools/javac/comp/Resolve.java | 25 +++++---- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 010deacdc36..5bb899ff5b1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4404,22 +4404,28 @@ public void visitIdent(JCIdent tree) { } result = checkId(tree, env1.enclClass.sym.type, sym, env, resultInfo); - - if (env.info.ctorPrologue && allowValueClasses) { - Resolve.FindEnclosingSelect findEnclosingSelect = rs.new FindEnclosingSelect(tree); - findEnclosingSelect.scan(env.tree); - // this identifier is standalone not part of a select - if (findEnclosingSelect.enclosingSelect == null) { - if (sym.owner != env.enclClass.sym || - TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { - // in this case we are seeing something like and access to `this`, as an identifier, in the prologue - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, tree.sym)) { - log.error(tree, Errors.CantRefBeforeCtorCalled(tree.sym)); + checkIfAllowedInPrologue(tree); + } + // where + void checkIfAllowedInPrologue(JCTree tree) { + Symbol sym = TreeInfo.symbolFor(tree); + if (env.info.ctorPrologue && allowValueClasses) { + JCFieldAccess enclosingSelect = rs.new FindEnclosingSelect().scan(tree, env.tree); + if (enclosingSelect == null) { // this tree is standalone, not part of a more complex name + if (sym.owner != env.enclClass.sym || + TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { + /* in this case we are seeing something like `super.field` or accessing a field of a + * super class while in the prologue of a subclass, at Resolve javac just didn't have enough + * information to determine this + */ + if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, sym) || + (tree.hasTag(SELECT) && localProxyVarsGen.hasAST(env.enclMethod, tree))) { + log.error(tree, Errors.CantRefBeforeCtorCalled(sym)); + } } } } } - } public void visitSelect(JCFieldAccess tree) { // Determine the expected kind of the qualifier expression. @@ -4555,27 +4561,7 @@ public void visitSelect(JCFieldAccess tree) { env.info.selectSuper = selectSuperPrev; result = checkId(tree, site, sym, env, resultInfo); - if (env.info.ctorPrologue && allowValueClasses) { - Resolve.FindEnclosingSelect findEnclosingSelect = rs.new FindEnclosingSelect(tree); - findEnclosingSelect.scan(env.tree); - // this is the top select - if (findEnclosingSelect.enclosingSelect == null) { - boolean error = false; - if (sym.owner != env.enclClass.sym || - TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { - // in this case we are seeing something like `super.field` or accessing - // a field of a super class while in the prologue - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, tree.sym) || - localProxyVarsGen.hasAST(env.enclMethod, tree)) { - log.error(tree, Errors.CantRefBeforeCtorCalled(tree.sym)); - error = true; - } - } - if (!error && localProxyVarsGen.hasAST(env.enclMethod, tree)) { - localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, tree.sym); - } - } - } + checkIfAllowedInPrologue(tree); } //where /** Determine symbol referenced by a Select expression, 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 36afd7745d1..e5e3d5e78c3 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 @@ -1545,18 +1545,20 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { switch (erk) { case UNDEFINED: if (allowValueClasses) { - /* at this point we don't have enough info, we are seeing a sub tree which could - * be part of a select or something bigger. Problem is that we will probably need - * to come back and remove the symbol from the corresponding data structure in - * LocalProxyVarsGen + /* at this point we don't have enough info, we could be seeing a sub tree which could + * be part of a select or something bigger */ JCTree tree = pos.getTree(); - FindEnclosingSelect findEnclosingSelect = new FindEnclosingSelect(tree); - findEnclosingSelect.scan(env1.tree); - tree = findEnclosingSelect.enclosingSelect == null ? tree : findEnclosingSelect.enclosingSelect; + JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env1.tree); + tree = enclosingSelect == null ? tree : enclosingSelect; if (tree == pos.getTree()) { + // we are seeing a standalone identifier referring to a field localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); } else { + /* we are seeing a component of a more complex expression which symbols are + * probably being determine now. Store the tree so that once we have the symbol + * we can evaluate if the access is permitted in the prologue or not + */ localProxyVarsGen.addASTReadInPrologue(env1.enclMethod, tree); } return sym; @@ -3916,9 +3918,8 @@ else if (env1.info.ctorPrologue) { case UNDEFINED: if (allowValueClasses) { JCTree tree = pos.getTree(); - FindEnclosingSelect findEnclosingSelect = new FindEnclosingSelect(tree); - findEnclosingSelect.scan(env1.tree); - tree = findEnclosingSelect.enclosingSelect == null ? tree : findEnclosingSelect.enclosingSelect; + JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env1.tree); + tree = enclosingSelect == null ? tree : enclosingSelect; if (tree == pos.getTree()) { localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); } else { @@ -3974,8 +3975,10 @@ public class FindEnclosingSelect extends TreeScanner { JCTree treeToLookFor; JCFieldAccess enclosingSelect = null; - FindEnclosingSelect(JCTree treeToLookFor) { + public JCFieldAccess scan(JCTree treeToLookFor, JCTree tree) { this.treeToLookFor = treeToLookFor; + super.scan(tree); + return enclosingSelect; } @Override From bc2e3d207193b1d95bf394d554adacfd7c97e8b3 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 23 Jun 2025 07:14:21 -0400 Subject: [PATCH 10/19] refacts --- .../com/sun/tools/javac/comp/Attr.java | 3 +- .../com/sun/tools/javac/comp/Resolve.java | 64 +++++++++---------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 5bb899ff5b1..ecc7e96360e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4408,6 +4408,7 @@ public void visitIdent(JCIdent tree) { } // where void checkIfAllowedInPrologue(JCTree tree) { + Assert.check(tree.hasTag(IDENT) || tree.hasTag(SELECT)); Symbol sym = TreeInfo.symbolFor(tree); if (env.info.ctorPrologue && allowValueClasses) { JCFieldAccess enclosingSelect = rs.new FindEnclosingSelect().scan(tree, env.tree); @@ -4419,7 +4420,7 @@ void checkIfAllowedInPrologue(JCTree tree) { * information to determine this */ if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, sym) || - (tree.hasTag(SELECT) && localProxyVarsGen.hasAST(env.enclMethod, tree))) { + (tree.hasTag(SELECT) && localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree))) { log.error(tree, Errors.CantRefBeforeCtorCalled(sym)); } } 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 e5e3d5e78c3..bc154cb9096 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 @@ -1545,28 +1545,12 @@ Symbol findVar(DiagnosticPosition pos, Env env, Name name) { switch (erk) { case UNDEFINED: if (allowValueClasses) { - /* at this point we don't have enough info, we could be seeing a sub tree which could - * be part of a select or something bigger - */ - JCTree tree = pos.getTree(); - JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env1.tree); - tree = enclosingSelect == null ? tree : enclosingSelect; - if (tree == pos.getTree()) { - // we are seeing a standalone identifier referring to a field - localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); - } else { - /* we are seeing a component of a more complex expression which symbols are - * probably being determine now. Store the tree so that once we have the symbol - * we can evaluate if the access is permitted in the prologue or not - */ - localProxyVarsGen.addASTReadInPrologue(env1.enclMethod, tree); - } + processUndefinedEarlyReference(pos.getTree(), env1, sym); return sym; } else { return new RefBeforeCtorCalledError(sym); } - case INSIDE_LAMBDA_OR_LOCAL_CLASS: - case NOT_ACCEPTABLE_OTHER: + case NOT_ACCEPTABLE: return new RefBeforeCtorCalledError(sym); default: return sym; @@ -1611,6 +1595,25 @@ else if (!bestSoFar.kind.betterThan(VAR)) { return bestSoFar; } + void processUndefinedEarlyReference(JCTree tree, Env env, Symbol sym) { + /* at this point we don't have enough info, we could be seeing a sub tree which could + * be part of a select or something bigger + */ + JCTree originalTree = tree; + JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env.tree); + tree = enclosingSelect == null ? originalTree : enclosingSelect; + if (tree == originalTree) { + // we are seeing a standalone tree + localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, sym); + } else { + /* we are seeing a component of a more complex expression which symbols are + * probably being determine now. Store the tree so that once we have the symbol + * we can evaluate if the access is permitted in the prologue or not + */ + localProxyVarsGen.addASTReadInPrologue(env.enclMethod, tree); + } + } + Warner noteWarner = new Warner(); /** Select the best method for a call site among two choices. @@ -3917,20 +3920,12 @@ else if (env1.info.ctorPrologue) { switch (erk) { case UNDEFINED: if (allowValueClasses) { - JCTree tree = pos.getTree(); - JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env1.tree); - tree = enclosingSelect == null ? tree : enclosingSelect; - if (tree == pos.getTree()) { - localProxyVarsGen.addFieldReadInPrologue(env1.enclMethod, sym); - } else { - localProxyVarsGen.addASTReadInPrologue(env1.enclMethod, tree); - } + processUndefinedEarlyReference(pos.getTree(), env1, sym); } else { sym = new RefBeforeCtorCalledError(sym); } break; - case INSIDE_LAMBDA_OR_LOCAL_CLASS: - case NOT_ACCEPTABLE_OTHER: + case NOT_ACCEPTABLE: sym = new RefBeforeCtorCalledError(sym); break; default: // do nothing @@ -4032,7 +4027,7 @@ private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env Date: Mon, 23 Jun 2025 09:46:11 -0400 Subject: [PATCH 11/19] refact --- .../classes/com/sun/tools/javac/comp/Flow.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 8bb726a1ce8..bad7ce4ab2d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -2233,20 +2233,25 @@ protected boolean trackable(VarSymbol sym) { return sym.pos >= startPos && ((sym.owner.kind == MTH || sym.owner.kind == VAR || - isFinalOrUninitializedField(sym))); + isTrackableField(sym))); } - boolean isFinalOrUninitializedField(VarSymbol sym) { + /* we want to track fields that are: + * - final regardless of "staticness" + * - non-final instance fields that lack an initializer + */ + boolean isTrackableField(VarSymbol sym) { return sym.owner.kind == TYP && (((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL || - (allowValueClasses && (sym.flags() & (HASINIT | PARAMETER)) == 0) && !sym.isStatic()) && + (allowValueClasses && (sym.flags() & HASINIT) == 0) && !sym.isStatic()) && classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); } // is non final instance field boolean isNonFinalUnitializedField(VarSymbol sym) { return sym.owner.kind == TYP && - ((!sym.isFinal() && (sym.flags() & (HASINIT | PARAMETER)) == 0 && + ((!sym.isFinal() && + (sym.flags() & HASINIT) == 0 && !sym.isStatic()) && classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); } @@ -3144,7 +3149,7 @@ public void visitApply(JCMethodInvocation tree) { else if (name == names._this) { for (int address = firstadr; address < nextadr; address++) { VarSymbol sym = vardecls[address].sym; - if (isFinalOrUninitializedField(sym) && !sym.isStatic()) + if (isTrackableField(sym) && !sym.isStatic()) letInit(tree.pos(), sym); } } From 680ead78c10011f0676d38943dfb110a89c0b79e Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 24 Jun 2025 15:11:44 -0400 Subject: [PATCH 12/19] more simplifications --- .../com/sun/tools/javac/comp/Attr.java | 3 +- .../tools/javac/comp/LocalProxyVarsGen.java | 35 ++++++------------- .../com/sun/tools/javac/comp/Resolve.java | 11 +----- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index ecc7e96360e..76f442116ad 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4419,8 +4419,7 @@ void checkIfAllowedInPrologue(JCTree tree) { * super class while in the prologue of a subclass, at Resolve javac just didn't have enough * information to determine this */ - if (localProxyVarsGen.removeSymReadInPrologue(env.enclMethod, sym) || - (tree.hasTag(SELECT) && localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree))) { + if (localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree)) { log.error(tree, Errors.CantRefBeforeCtorCalled(sym)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java index dee5ed2aceb..8a7846477a7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java @@ -32,6 +32,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; @@ -90,8 +91,7 @@ public static LocalProxyVarsGen instance(Context context) { private final UnsetFieldsInfo unsetFieldsInfo; private ClassSymbol currentClass = null; private java.util.List instanceFields; - private Map> fieldsReadInPrologue = new HashMap<>(); - private Map> ASTsReadInPrologue = new HashMap<>(); + private Map> ASTsReferencedInPrologue = new HashMap<>(); private final boolean noLocalProxyVars; @@ -107,29 +107,15 @@ protected LocalProxyVarsGen(Context context) { noLocalProxyVars = options.isSet("noLocalProxyVars"); } - public void addFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) { - Set fieldSet = fieldsReadInPrologue.getOrDefault(constructor, new HashSet<>()); - fieldSet.add(sym); - fieldsReadInPrologue.put(constructor, fieldSet); - } - - public boolean removeSymReadInPrologue(JCMethodDecl constructor, Symbol sym) { - Set fieldSet = fieldsReadInPrologue.get(constructor); - if (fieldSet != null) { - return fieldSet.remove(sym); - } - return false; - } - public void addASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { // better to have order for this one - Set treeSet = ASTsReadInPrologue.getOrDefault(constructor, new LinkedHashSet<>()); + Set treeSet = ASTsReferencedInPrologue.getOrDefault(constructor, new LinkedHashSet<>()); treeSet.add(tree); - ASTsReadInPrologue.put(constructor, treeSet); + ASTsReferencedInPrologue.put(constructor, treeSet); } public boolean removeASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { - Set treeSet = ASTsReadInPrologue.get(constructor); + Set treeSet = ASTsReferencedInPrologue.get(constructor); if (treeSet != null) { return treeSet.remove(tree); } @@ -137,7 +123,7 @@ public boolean removeASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { } public boolean hasAST(JCMethodDecl constructor, JCTree tree) { - Set treeSet = ASTsReadInPrologue.get(constructor); + Set treeSet = ASTsReferencedInPrologue.get(constructor); if (treeSet != null) { return treeSet.contains(tree); } @@ -177,16 +163,17 @@ public void visitClassDef(JCClassDecl tree) { } public void visitMethodDef(JCMethodDecl tree) { - if (fieldsReadInPrologue.get(tree) != null) { - Set fieldSet = fieldsReadInPrologue.get(tree); + if (ASTsReferencedInPrologue.get(tree) != null) { + Set ASTSet = ASTsReferencedInPrologue.get(tree); java.util.List strictFieldsRead = new ArrayList<>(); + Set symbols = ASTSet.stream().map(t -> TreeInfo.symbolFor(t)).collect(Collectors.toSet()); for (JCVariableDecl sfield : instanceFields) { - if (fieldSet.contains(sfield.sym)) { + if (symbols.contains(sfield.sym)) { strictFieldsRead.add(sfield); } } addLocalProxiesFor(tree, strictFieldsRead); - fieldsReadInPrologue.remove(tree); + ASTsReferencedInPrologue.remove(tree); } super.visitMethodDef(tree); } 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 bc154cb9096..acb504cb20b 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 @@ -1602,16 +1602,7 @@ void processUndefinedEarlyReference(JCTree tree, Env env, Symbol sy JCTree originalTree = tree; JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env.tree); tree = enclosingSelect == null ? originalTree : enclosingSelect; - if (tree == originalTree) { - // we are seeing a standalone tree - localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, sym); - } else { - /* we are seeing a component of a more complex expression which symbols are - * probably being determine now. Store the tree so that once we have the symbol - * we can evaluate if the access is permitted in the prologue or not - */ - localProxyVarsGen.addASTReadInPrologue(env.enclMethod, tree); - } + localProxyVarsGen.addASTReadInPrologue(env.enclMethod, tree); } Warner noteWarner = new Warner(); From 1a9b97741a3bed445ff8cd181eb6f5ccbece4fbd Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 24 Jun 2025 15:11:44 -0400 Subject: [PATCH 13/19] more simplifications --- .../com/sun/tools/javac/comp/Attr.java | 7 ++-- .../tools/javac/comp/LocalProxyVarsGen.java | 35 ++++++------------- .../com/sun/tools/javac/comp/Resolve.java | 11 +----- 3 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index ecc7e96360e..b95f6aaae43 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -336,8 +336,10 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env instanceFields; - private Map> fieldsReadInPrologue = new HashMap<>(); - private Map> ASTsReadInPrologue = new HashMap<>(); + private Map> ASTsReferencedInPrologue = new HashMap<>(); private final boolean noLocalProxyVars; @@ -107,29 +107,15 @@ protected LocalProxyVarsGen(Context context) { noLocalProxyVars = options.isSet("noLocalProxyVars"); } - public void addFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) { - Set fieldSet = fieldsReadInPrologue.getOrDefault(constructor, new HashSet<>()); - fieldSet.add(sym); - fieldsReadInPrologue.put(constructor, fieldSet); - } - - public boolean removeSymReadInPrologue(JCMethodDecl constructor, Symbol sym) { - Set fieldSet = fieldsReadInPrologue.get(constructor); - if (fieldSet != null) { - return fieldSet.remove(sym); - } - return false; - } - public void addASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { // better to have order for this one - Set treeSet = ASTsReadInPrologue.getOrDefault(constructor, new LinkedHashSet<>()); + Set treeSet = ASTsReferencedInPrologue.getOrDefault(constructor, new LinkedHashSet<>()); treeSet.add(tree); - ASTsReadInPrologue.put(constructor, treeSet); + ASTsReferencedInPrologue.put(constructor, treeSet); } public boolean removeASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { - Set treeSet = ASTsReadInPrologue.get(constructor); + Set treeSet = ASTsReferencedInPrologue.get(constructor); if (treeSet != null) { return treeSet.remove(tree); } @@ -137,7 +123,7 @@ public boolean removeASTReadInPrologue(JCMethodDecl constructor, JCTree tree) { } public boolean hasAST(JCMethodDecl constructor, JCTree tree) { - Set treeSet = ASTsReadInPrologue.get(constructor); + Set treeSet = ASTsReferencedInPrologue.get(constructor); if (treeSet != null) { return treeSet.contains(tree); } @@ -177,16 +163,17 @@ public void visitClassDef(JCClassDecl tree) { } public void visitMethodDef(JCMethodDecl tree) { - if (fieldsReadInPrologue.get(tree) != null) { - Set fieldSet = fieldsReadInPrologue.get(tree); + if (ASTsReferencedInPrologue.get(tree) != null) { + Set ASTSet = ASTsReferencedInPrologue.get(tree); java.util.List strictFieldsRead = new ArrayList<>(); + Set symbols = ASTSet.stream().map(t -> TreeInfo.symbolFor(t)).collect(Collectors.toSet()); for (JCVariableDecl sfield : instanceFields) { - if (fieldSet.contains(sfield.sym)) { + if (symbols.contains(sfield.sym)) { strictFieldsRead.add(sfield); } } addLocalProxiesFor(tree, strictFieldsRead); - fieldsReadInPrologue.remove(tree); + ASTsReferencedInPrologue.remove(tree); } super.visitMethodDef(tree); } 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 bc154cb9096..acb504cb20b 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 @@ -1602,16 +1602,7 @@ void processUndefinedEarlyReference(JCTree tree, Env env, Symbol sy JCTree originalTree = tree; JCFieldAccess enclosingSelect = new FindEnclosingSelect().scan(tree, env.tree); tree = enclosingSelect == null ? originalTree : enclosingSelect; - if (tree == originalTree) { - // we are seeing a standalone tree - localProxyVarsGen.addFieldReadInPrologue(env.enclMethod, sym); - } else { - /* we are seeing a component of a more complex expression which symbols are - * probably being determine now. Store the tree so that once we have the symbol - * we can evaluate if the access is permitted in the prologue or not - */ - localProxyVarsGen.addASTReadInPrologue(env.enclMethod, tree); - } + localProxyVarsGen.addASTReadInPrologue(env.enclMethod, tree); } Warner noteWarner = new Warner(); From 83b9255624b01e3c53796be5b17679bbea86aaa3 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 2 Jul 2025 17:56:24 -0400 Subject: [PATCH 14/19] updating tests --- .../tools/javac/SuperInit/EarlyAssignments.out | 3 +-- .../tools/javac/diags/examples.not-yet.txt | 1 + ...java => CantWriteFieldFromLocalInPrologue.java} | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) rename test/langtools/tools/javac/diags/examples/{CantAssignInitializedBeforeCtorCalled.java => CantWriteFieldFromLocalInPrologue.java} (84%) diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out index 26780b1e3ab..2b2e70aee7f 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out @@ -15,9 +15,8 @@ EarlyAssignments.java:117:22: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:122:29: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:127:22: compiler.err.cant.ref.before.ctor.called: x EarlyAssignments.java:132:28: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:141:13: compiler.err.cant.assign.initialized.before.ctor.called: x EarlyAssignments.java:150:13: compiler.err.cant.assign.val.to.var: final, x EarlyAssignments.java:161:13: compiler.err.cant.ref.before.ctor.called: this - compiler.note.preview.filename: EarlyAssignments.java, DEFAULT - compiler.note.preview.recompile -20 errors +19 errors diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 2a909df2e3c..03c057e04c7 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -226,3 +226,4 @@ compiler.misc.feature.value.classes # Pending removal compiler.note.implicit.annotation.processing compiler.warn.proc.use.proc.or.implicit +compiler.err.cant.assign.initialized.before.ctor.called diff --git a/test/langtools/tools/javac/diags/examples/CantAssignInitializedBeforeCtorCalled.java b/test/langtools/tools/javac/diags/examples/CantWriteFieldFromLocalInPrologue.java similarity index 84% rename from test/langtools/tools/javac/diags/examples/CantAssignInitializedBeforeCtorCalled.java rename to test/langtools/tools/javac/diags/examples/CantWriteFieldFromLocalInPrologue.java index 55f5d19451d..a6b1e8b1a91 100644 --- a/test/langtools/tools/javac/diags/examples/CantAssignInitializedBeforeCtorCalled.java +++ b/test/langtools/tools/javac/diags/examples/CantWriteFieldFromLocalInPrologue.java @@ -23,13 +23,17 @@ // key: compiler.note.preview.filename // key: compiler.note.preview.recompile - // key: compiler.err.cant.assign.initialized.before.ctor.called + // key: compiler.err.cant.ref.before.ctor.called // options: --enable-preview -source ${jdk.version} -class CantAssignInitializedBeforeCtorCalled { - int x = 1; - CantAssignInitializedBeforeCtorCalled() { - x = 2; +class CantWriteFieldFromLocalInPrologue { + int x; + CantWriteFieldFromLocalInPrologue() { + class Local { + { + x++; + } + } super(); } } From 95fb2a8096c1210ce8d3968f7021f1186e5658e6 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 3 Jul 2025 11:23:39 -0400 Subject: [PATCH 15/19] adding comments, etc --- .../com/sun/tools/javac/comp/Attr.java | 16 ------ .../com/sun/tools/javac/comp/Flow.java | 13 +---- .../com/sun/tools/javac/comp/Resolve.java | 49 +++++++++++++++++-- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index b95f6aaae43..69f5799669e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -327,22 +327,6 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env env, Name name) { } case NOT_ACCEPTABLE: return new RefBeforeCtorCalledError(sym); + case FIELD_HAS_INIT: + if (!allowValueClasses) { + return new RefBeforeCtorCalledError(sym, true); + } else { + // acceptable in valhalla + return sym; + } default: return sym; } @@ -3919,6 +3926,11 @@ else if (env1.info.ctorPrologue) { case NOT_ACCEPTABLE: sym = new RefBeforeCtorCalledError(sym); break; + case FIELD_HAS_INIT: + if (!allowValueClasses) { + sym = new RefBeforeCtorCalledError(sym, true); + } + break; default: // do nothing } } @@ -4058,14 +4070,29 @@ private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env Date: Fri, 11 Jul 2025 17:16:49 -0400 Subject: [PATCH 16/19] do not generate proxies for mutable fields --- .../com/sun/tools/javac/comp/Attr.java | 15 +++++---- .../javac/SuperInit/EarlyAssignments.java | 20 +++++++++++ .../javac/SuperInit/EarlyAssignments2.java | 33 ------------------- .../javac/SuperInit/EarlyAssignments2.out | 7 ---- 4 files changed, 29 insertions(+), 46 deletions(-) delete mode 100644 test/langtools/tools/javac/SuperInit/EarlyAssignments2.java delete mode 100644 test/langtools/tools/javac/SuperInit/EarlyAssignments2.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 69f5799669e..2dbc0ff15aa 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4399,14 +4399,17 @@ void checkIfAllowedInPrologue(JCTree tree) { if (env.info.ctorPrologue && allowValueClasses) { JCFieldAccess enclosingSelect = rs.new FindEnclosingSelect().scan(tree, env.tree); if (enclosingSelect == null) { // this tree is standalone, not part of a more complex name - if (sym.owner != env.enclClass.sym || + if (localProxyVarsGen.hasAST(env.enclMethod, tree)) { + if (sym.owner != env.enclClass.sym || TreeInfo.isExplicitThisOrSuperReference(types, (ClassType)env.enclClass.type, tree)) { - /* in this case we are seeing something like `super.field` or accessing a field of a - * super class while in the prologue of a subclass, at Resolve javac just didn't have enough - * information to determine this - */ - if (localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree)) { + /* in this case we are seeing something like `super.field` or accessing a field of a + * super class while in the prologue of a subclass, at Resolve javac just didn't have enough + * information to determine this + */ + localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree); log.error(tree, Errors.CantRefBeforeCtorCalled(sym)); + } else if (!sym.isFinal() && !sym.isStrict()) { + localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree); } } } diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java index 0d7c65a7c21..43926b93aaa 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.java +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.java @@ -10,6 +10,26 @@ public class EarlyAssignments { public static class Inner1 { public int x; + public Inner1(int y) { + y = x; // OK mutable field + super(); + } + + public Inner1(int y, int ignore1) { + y = this.x; // OK mutable field + super(); + } + + public Inner1(int y, boolean ignore2) { + y = Inner1.this.x; // OK mutable field + super(); + } + + public Inner1(short[] x) { + this.x++; // OK mutable field + super(); + } + public Inner1() { x = 123; // OK - "x" belongs to this class this.x = 123; // OK - "x" belongs to this class diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java deleted file mode 100644 index 43751734657..00000000000 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @bug 8359370 - * @summary [lworld] allow instance fields of identity classes to be readable in the prologue phase - * @compile/fail/ref=EarlyAssignments2.out -XDrawDiagnostics EarlyAssignments2.java - * @enablePreview - */ - -public class EarlyAssignments2 { - public static class Inner1 { - public int x; - - public Inner1(int y) { - y = x; // FAIL - x might not have been initialized - super(); - } - - public Inner1(int y, int ignore1) { - y = this.x; // FAIL - x might not have been initialized - super(); - } - - public Inner1(int y, boolean ignore2) { - y = Inner1.this.x; // FAIL - x might not have been initialized - super(); - } - - public Inner1(short[] x) { - this.x++; // this should FAIL - super(); - } - } -} diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out deleted file mode 100644 index 257aa3e4909..00000000000 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments2.out +++ /dev/null @@ -1,7 +0,0 @@ -EarlyAssignments2.java:14:17: compiler.err.var.might.not.have.been.initialized: x -EarlyAssignments2.java:19:21: compiler.err.var.might.not.have.been.initialized: x -EarlyAssignments2.java:24:28: compiler.err.var.might.not.have.been.initialized: x -EarlyAssignments2.java:29:17: compiler.err.var.might.not.have.been.initialized: x -- compiler.note.preview.filename: EarlyAssignments2.java, DEFAULT -- compiler.note.preview.recompile -4 errors From f3f2913aeb1849a962b62874dc5429124efda996 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 15 Jul 2025 11:10:07 -0400 Subject: [PATCH 17/19] updating golden file --- .../javac/SuperInit/EarlyAssignments.out | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out index 2b2e70aee7f..afa06d03298 100644 --- a/test/langtools/tools/javac/SuperInit/EarlyAssignments.out +++ b/test/langtools/tools/javac/SuperInit/EarlyAssignments.out @@ -1,22 +1,22 @@ -EarlyAssignments.java:24:26: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:25:21: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:26:26: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:27:34: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:29:36: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:33:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:37:21: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:41:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:59:13: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:60:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:61:25: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:62:31: compiler.err.cant.ref.before.ctor.called: this -EarlyAssignments.java:112:17: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:117:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:122:29: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:127:22: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:132:28: compiler.err.cant.ref.before.ctor.called: x -EarlyAssignments.java:150:13: compiler.err.cant.assign.val.to.var: final, x -EarlyAssignments.java:161:13: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:44:26: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:45:21: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:46:26: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:47:34: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:49:36: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:53:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:57:21: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:61:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:79:13: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:80:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:81:25: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:82:31: compiler.err.cant.ref.before.ctor.called: this +EarlyAssignments.java:132:17: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:137:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:142:29: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:147:22: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:152:28: compiler.err.cant.ref.before.ctor.called: x +EarlyAssignments.java:170:13: compiler.err.cant.assign.val.to.var: final, x +EarlyAssignments.java:181:13: compiler.err.cant.ref.before.ctor.called: this - compiler.note.preview.filename: EarlyAssignments.java, DEFAULT - compiler.note.preview.recompile 19 errors From 475dc7107743fe89341890dd8b11628a6888c0d5 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 15 Jul 2025 12:16:39 -0400 Subject: [PATCH 18/19] adding @enablePreview to some tests --- test/langtools/tools/javac/T7093325.java | 1 + test/langtools/tools/javac/T8222949/TestConstantDynamic.java | 1 + test/langtools/tools/javac/lambda/TestInvokeDynamic.java | 1 + .../tools/javac/lambda/bytecode/TestLambdaBytecode.java | 1 + .../javac/lambda/bytecode/TestLambdaBytecodeTargetRelease14.java | 1 + test/langtools/tools/javac/preview/PreviewErrors.java | 1 + test/langtools/tools/javac/varargs/7042566/T7042566.java | 1 + 7 files changed, 7 insertions(+) diff --git a/test/langtools/tools/javac/T7093325.java b/test/langtools/tools/javac/T7093325.java index 2dfb171ea70..5e6dd5ae95a 100644 --- a/test/langtools/tools/javac/T7093325.java +++ b/test/langtools/tools/javac/T7093325.java @@ -32,6 +32,7 @@ * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper * @run main T7093325 + * @enablePreview */ import java.io.IOException; diff --git a/test/langtools/tools/javac/T8222949/TestConstantDynamic.java b/test/langtools/tools/javac/T8222949/TestConstantDynamic.java index 4578bfbf318..838ec171370 100644 --- a/test/langtools/tools/javac/T8222949/TestConstantDynamic.java +++ b/test/langtools/tools/javac/T8222949/TestConstantDynamic.java @@ -34,6 +34,7 @@ * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper * @run main TestConstantDynamic + * @enablePreview */ import java.io.IOException; diff --git a/test/langtools/tools/javac/lambda/TestInvokeDynamic.java b/test/langtools/tools/javac/lambda/TestInvokeDynamic.java index b7e980d5726..1f60f9e9dae 100644 --- a/test/langtools/tools/javac/lambda/TestInvokeDynamic.java +++ b/test/langtools/tools/javac/lambda/TestInvokeDynamic.java @@ -35,6 +35,7 @@ * jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper + * @enablePreview * @run main TestInvokeDynamic */ diff --git a/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java b/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java index 4c779b2b6c3..ab7f31b6d50 100644 --- a/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java +++ b/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java @@ -31,6 +31,7 @@ * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper + * @enablePreview * @run main TestLambdaBytecode */ diff --git a/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecodeTargetRelease14.java b/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecodeTargetRelease14.java index ac67054d732..40fe216b27d 100644 --- a/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecodeTargetRelease14.java +++ b/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecodeTargetRelease14.java @@ -31,6 +31,7 @@ * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper + * @enablePreview * @run main TestLambdaBytecodeTargetRelease14 */ diff --git a/test/langtools/tools/javac/preview/PreviewErrors.java b/test/langtools/tools/javac/preview/PreviewErrors.java index eab5b2af9bf..2a475f3ec30 100644 --- a/test/langtools/tools/javac/preview/PreviewErrors.java +++ b/test/langtools/tools/javac/preview/PreviewErrors.java @@ -33,6 +33,7 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.compiler/com.sun.tools.javac.util * @build toolbox.ToolBox toolbox.JavacTask + * @enablePreview * @build combo.ComboTestHelper * @run main PreviewErrors */ diff --git a/test/langtools/tools/javac/varargs/7042566/T7042566.java b/test/langtools/tools/javac/varargs/7042566/T7042566.java index 1f0e6b768ba..1dbbaa7adfc 100644 --- a/test/langtools/tools/javac/varargs/7042566/T7042566.java +++ b/test/langtools/tools/javac/varargs/7042566/T7042566.java @@ -31,6 +31,7 @@ * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper + * @enablePreview * @run main T7042566 */ From 4f0959c94cd578ee33d02b511f1e80fea7ec8b26 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 16 Jul 2025 12:47:38 -0400 Subject: [PATCH 19/19] addressing review comments --- .../classes/com/sun/tools/javac/comp/Resolve.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) 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 4bb37f66f41..51c1d7c102d 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 @@ -4029,19 +4029,9 @@ private EarlyReferenceKind isAllowedEarlyReference(DiagnosticPosition pos, Env