Skip to content

8359370: [lworld] allow instance fields of identity classes to be readable in the prologue phase #1490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: lworld
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -317,20 +319,14 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env<AttrC
return;
}

// Check instance field assignments that appear in constructor prologues
// Check instance field assignments that appear in constructor prologues, like: `this.field = value;`
if (rs.isEarlyReference(env, base, v)) {

// Field may not be inherited from a superclass
if (v.owner != env.enclClass.sym) {
log.error(pos, Errors.CantRefBeforeCtorCalled(v));
return;
}

// Field may not have an initializer
if ((v.flags() & HASINIT) != 0) {
log.error(pos, Errors.CantAssignInitializedBeforeCtorCalled(v));
return;
}
}
}

Expand Down Expand Up @@ -958,7 +954,9 @@ public void visitClassDef(JCClassDecl tree) {
Optional.ofNullable(env.info.attributionMode.isSpeculative ?
argumentAttr.withLocalCacheContext() : null);
boolean ctorProloguePrev = env.info.ctorPrologue;
JCClassDecl localClassPrev = env.info.localClass;
try {
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)) {
Expand Down Expand Up @@ -992,6 +990,7 @@ public void visitClassDef(JCClassDecl tree) {
} finally {
localCacheContext.ifPresent(LocalCacheContext::leave);
env.info.ctorPrologue = ctorProloguePrev;
env.info.localClass = localClassPrev;
}
}

Expand Down Expand Up @@ -4391,7 +4390,31 @@ public void visitIdent(JCIdent tree) {
}

result = checkId(tree, env1.enclClass.sym.type, sym, env, resultInfo);
checkIfAllowedInPrologue(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);
if (enclosingSelect == null) { // this tree is standalone, not part of a more complex name
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
*/
localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree);
log.error(tree, Errors.CantRefBeforeCtorCalled(sym));
} else if (!sym.isFinal() && !sym.isStrict()) {
localProxyVarsGen.removeASTReadInPrologue(env.enclMethod, tree);
}
}
}
}
}

public void visitSelect(JCFieldAccess tree) {
// Determine the expected kind of the qualifier expression.
Expand Down Expand Up @@ -4527,6 +4550,7 @@ public void visitSelect(JCFieldAccess tree) {

env.info.selectSuper = selectSuperPrev;
result = checkId(tree, site, sym, env, resultInfo);
checkIfAllowedInPrologue(tree);
}
//where
/** Determine symbol referenced by a Select expression,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ public class AttrContext {

boolean instanceInitializerBlock = false;

/**
* Local class being attributed if any
*/
JCTree.JCClassDecl localClass = null;

/** Duplicate this context, replacing scope field and copying all others.
*/
AttrContext dup(WriteableScope scope) {
Expand All @@ -157,6 +162,7 @@ AttrContext dup(WriteableScope scope) {
info.allowProtectedAccess = allowProtectedAccess;
info.instanceInitializerBlock = instanceInitializerBlock;
info.isPermitsClause = isPermitsClause;
info.localClass = localClass;
return info;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
70 changes: 48 additions & 22 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ public class Flow {
private Env<AttrContext> attrEnv;
private Lint lint;
private final Infer infer;
private final LocalProxyVarsGen localProxyVarsGen;
private final UnsetFieldsInfo unsetFieldsInfo;
private final boolean allowValueClasses;

Expand Down Expand Up @@ -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()) &&
Expand Down Expand Up @@ -487,15 +489,21 @@ 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<? super JCTree> 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.
* 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.
*/
protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, boolean earlyOnly,
protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, InitializerDisc discriminator,
Consumer<? super JCTree> handler) {
if (classDef == initScanClass) // avoid infinite loops
return;
Expand All @@ -515,15 +523,18 @@ 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.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);
}
}
}
Expand Down Expand Up @@ -2205,6 +2216,7 @@ public AssignAnalyzer() {

private boolean isConstructor;
private boolean isCompactOrGeneratedRecordConstructor;
private JCMethodDecl currentMethod;

@Override
protected void markDead() {
Expand All @@ -2221,13 +2233,17 @@ protected boolean trackable(VarSymbol sym) {
return
sym.pos >= startPos &&
((sym.owner.kind == MTH || sym.owner.kind == VAR ||
isFinalOrStrictUninitializedField(sym)));
isTrackableField(sym)));
}

boolean isFinalOrStrictUninitializedField(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 ||
(sym.flags() & (STRICT | HASINIT | PARAMETER)) == STRICT) &&
(sym.flags() & (STRICT | HASINIT | PARAMETER)) == STRICT) &&
classDef.sym.isEnclosedBy((ClassSymbol)sym.owner));
}

Expand Down Expand Up @@ -2528,10 +2544,12 @@ public void visitMethodDef(JCMethodDecl tree) {
Assert.check(pendingExits.isEmpty());
boolean isConstructorPrev = isConstructor;
boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor;
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));
currentMethod = tree;

// We only track field initialization inside constructors
if (!isConstructor) {
Expand Down Expand Up @@ -2560,15 +2578,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) {
Expand Down Expand Up @@ -2603,6 +2624,7 @@ public void visitMethodDef(JCMethodDecl tree) {
returnadr = returnadrPrev;
isConstructor = isConstructorPrev;
isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev;
currentMethod = currentMethodPrev;
}
} finally {
lint = lintPrev;
Expand Down Expand Up @@ -3086,7 +3108,7 @@ 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);
});
Expand All @@ -3108,7 +3130,7 @@ 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);
});
Expand All @@ -3118,7 +3140,7 @@ public void visitApply(JCMethodInvocation tree) {
else if (name == names._this) {
for (int address = firstadr; address < nextadr; address++) {
VarSymbol sym = vardecls[address].sym;
if (isFinalOrStrictUninitializedField(sym) && !sym.isStatic())
if (isTrackableField(sym) && !sym.isStatic())
letInit(tree.pos(), sym);
}
}
Expand Down Expand Up @@ -3199,8 +3221,12 @@ public void visitAssign(JCAssign tree) {
// assigned before reading their value
public void visitSelect(JCFieldAccess tree) {
super.visitSelect(tree);
if (TreeInfo.isThisQualifier(tree.selected) &&
tree.sym.kind == VAR) {
if (TreeInfo.isThisQualifier(tree.selected) && tree.sym.kind == VAR) {
if (trackable((VarSymbol)tree.sym)) {
checkInit(tree.pos(), (VarSymbol) tree.sym);
}
}
if (localProxyVarsGen.hasAST(currentMethod, tree)) {
checkInit(tree.pos(), (VarSymbol)tree.sym);
}
}
Expand Down
Loading