< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Print this page
final Dependencies dependencies;
final Annotate annotate;
final ArgumentAttr argumentAttr;
final MatchBindingsComputer matchBindingsComputer;
final AttrRecover attrRecover;
+ final LocalProxyVarsGen localProxyVarsGen;
public static Attr instance(Context context) {
Attr instance = context.get(attrKey);
if (instance == null)
instance = new Attr(context);
typeEnvs = TypeEnvs.instance(context);
dependencies = Dependencies.instance(context);
argumentAttr = ArgumentAttr.instance(context);
matchBindingsComputer = MatchBindingsComputer.instance(context);
attrRecover = AttrRecover.instance(context);
+ localProxyVarsGen = LocalProxyVarsGen.instance(context);
Options options = Options.instance(context);
Source source = Source.instance(context);
allowReifiableTypesInInstanceof = Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source);
methodAttrInfo = new MethodAttrInfo();
unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType);
unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType);
recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
initBlockType = new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass);
+ allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
+ Feature.VALUE_CLASSES.allowedInSource(source);
}
/** Switch: reifiable types in instanceof enabled?
*/
boolean allowReifiableTypesInInstanceof;
/** Are unconditional patterns in instanceof allowed
*/
private final boolean allowUnconditionalPatternsInstanceOf;
+ /** Are value classes allowed
+ */
+ private final boolean allowValueClasses;
+
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
*/
boolean useBeforeDeclarationWarning;
* @param env The current environment.
*/
void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env<AttrContext> env) {
if (v.name == names._this) {
log.error(pos, Errors.CantAssignValToThis);
- return;
- }
- if ((v.flags() & FINAL) != 0 &&
+ } else if ((v.flags() & FINAL) != 0 &&
((v.flags() & HASINIT) != 0
||
!((base == null ||
TreeInfo.isThisQualifier(base)) &&
isAssignableAsBlankFinal(v, env)))) {
if (v.isResourceVariable()) { //TWR resource
log.error(pos, Errors.TryResourceMayNotBeAssigned(v));
} else {
log.error(pos, Errors.CantAssignValToVar(Flags.toSource(v.flags() & (STATIC | FINAL)), v));
}
- return;
- }
-
- // Check instance field assignments that appear in constructor prologues
- 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;
- }
}
}
/** Does tree represent a static reference to an identifier?
* It is assumed that tree is either a SELECT or an IDENT.
Fragments.CanonicalMustNotHaveStrongerAccess(asFlagSet(env.enclClass.sym.flags() & AccessFlags))
)
);
}
- if (TreeInfo.hasAnyConstructorCall(tree)) {
+ if (!allowValueClasses && TreeInfo.hasAnyConstructorCall(tree)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name,
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
}
}
// or we are compiling class java.lang.Object.
if (isConstructor && owner.type != syms.objectType) {
if (!TreeInfo.hasAnyConstructorCall(tree)) {
JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(),
make.Ident(names._super), make.Idents(List.nil())));
- tree.body.stats = tree.body.stats.prepend(supCall);
+ if (allowValueClasses && (owner.isValueClass() || owner.hasStrict() || ((owner.flags_field & RECORD) != 0))) {
+ tree.body.stats = tree.body.stats.append(supCall);
+ } else {
+ tree.body.stats = tree.body.stats.prepend(supCall);
+ }
} else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
(tree.mods.flags & GENERATEDCONSTR) == 0 &&
TreeInfo.hasConstructorCall(tree, names._super)) {
// enum constructors are not allowed to call super
// directly, so make sure there aren't any super calls
// Start of constructor prologue
localEnv.info.ctorPrologue = isConstructor;
// Attribute method body.
attribStat(tree.body, localEnv);
+ if (isConstructor) {
+ ListBuffer<JCTree> prologueCode = new ListBuffer<>();
+ for (JCTree stat : tree.body.stats) {
+ prologueCode.add(stat);
+ /* gather all the stats in the body until a `super` or `this` constructor invocation is found,
+ * including the constructor invocation, that way we don't need to worry in the visitor below if
+ * if we are dealing or not with prologue code
+ */
+ if (stat instanceof JCExpressionStatement expStmt &&
+ expStmt.expr instanceof JCMethodInvocation mi &&
+ TreeInfo.isConstructorCall(mi)) {
+ break;
+ }
+ }
+ if (!prologueCode.isEmpty()) {
+ CtorPrologueVisitor ctorPrologueVisitor = new CtorPrologueVisitor(localEnv);
+ ctorPrologueVisitor.scan(prologueCode.toList());
+ }
+ }
}
localEnv.info.scope.leave();
result = tree.type = m.type;
} finally {
chk.setMethod(prevMethod);
env.info.ctorPrologue = ctorProloguePrev;
}
}
+ class CtorPrologueVisitor extends TreeScanner {
+ Env<AttrContext> localEnv;
+ CtorPrologueVisitor(Env<AttrContext> localEnv) {
+ this.localEnv = localEnv;
+ }
+
+ boolean insideLambdaOrClassDef = false;
+
+ @Override
+ public void visitLambda(JCLambda lambda) {
+ boolean previousInsideLambdaOrClassDef = insideLambdaOrClassDef;
+ try {
+ insideLambdaOrClassDef = true;
+ super.visitLambda(lambda);
+ } finally {
+ insideLambdaOrClassDef = previousInsideLambdaOrClassDef;
+ }
+ }
+
+ @Override
+ public void visitClassDef(JCClassDecl classDecl) {
+ boolean previousInsideLambdaOrClassDef = insideLambdaOrClassDef;
+ try {
+ insideLambdaOrClassDef = true;
+ super.visitClassDef(classDecl);
+ } finally {
+ insideLambdaOrClassDef = previousInsideLambdaOrClassDef;
+ }
+ }
+
+ private void reportPrologueError(JCTree tree, Symbol sym) {
+ preview.checkSourceLevel(tree, Feature.FLEXIBLE_CONSTRUCTORS);
+ log.error(tree, Errors.CantRefBeforeCtorCalled(sym));
+ }
+
+ @Override
+ public void visitApply(JCMethodInvocation tree) {
+ super.visitApply(tree);
+ Name name = TreeInfo.name(tree.meth);
+ boolean isConstructorCall = name == names._this || name == names._super;
+ Symbol msym = TreeInfo.symbolFor(tree.meth);
+ // is this an instance method call or an illegal constructor invocation like: `this.super()`?
+ if (msym != null && // for erroneous invocations msym can be null, ignore those
+ (!isConstructorCall ||
+ isConstructorCall && tree.meth.hasTag(SELECT))) {
+ if (isEarlyReference(localEnv, tree.meth, msym))
+ reportPrologueError(tree.meth, msym);
+ }
+ }
+
+ @Override
+ public void visitIdent(JCIdent tree) {
+ analyzeSymbol(tree);
+ }
+
+ @Override
+ public void visitSelect(JCFieldAccess tree) {
+ SelectScanner ss = new SelectScanner();
+ ss.scan(tree);
+ if (ss.scanLater == null) {
+ analyzeSymbol(tree);
+ } else {
+ boolean prevLhs = isInLHS;
+ try {
+ isInLHS = false;
+ scan(ss.scanLater);
+ } finally {
+ isInLHS = prevLhs;
+ }
+ }
+ }
+
+ @Override
+ public void visitNewClass(JCNewClass tree) {
+ super.visitNewClass(tree);
+ checkNewClassAndMethRefs(tree, tree.type);
+ }
+
+ @Override
+ public void visitReference(JCMemberReference tree) {
+ super.visitReference(tree);
+ if (tree.getMode() == JCMemberReference.ReferenceMode.NEW) {
+ checkNewClassAndMethRefs(tree, tree.expr.type);
+ }
+ }
+
+ void checkNewClassAndMethRefs(JCTree tree, Type t) {
+ if (t.tsym.isEnclosedBy(localEnv.enclClass.sym) &&
+ !t.tsym.isStatic() &&
+ !t.tsym.isDirectlyOrIndirectlyLocal()) {
+ reportPrologueError(tree, t.getEnclosingType().tsym);
+ }
+ }
+
+ /* if a symbol is in the LHS of an assignment expression we won't consider it as a candidate
+ * for a proxy local variable later on
+ */
+ boolean isInLHS = false;
+
+ @Override
+ public void visitAssign(JCAssign tree) {
+ boolean previousIsInLHS = isInLHS;
+ try {
+ isInLHS = true;
+ scan(tree.lhs);
+ } finally {
+ isInLHS = previousIsInLHS;
+ }
+ scan(tree.rhs);
+ }
+
+ @Override
+ public void visitMethodDef(JCMethodDecl tree) {
+ // ignore any declarative part, mainly to avoid scanning receiver parameters
+ scan(tree.body);
+ }
+
+ void analyzeSymbol(JCTree tree) {
+ Symbol sym = TreeInfo.symbolFor(tree);
+ if (isInLHS && !insideLambdaOrClassDef) {
+ // Check instance field assignments that appear in constructor prologues
+ if (isEarlyReference(localEnv, tree, sym)) {
+ // Field may not be inherited from a superclass
+ if (sym.owner != localEnv.enclClass.sym) {
+ log.error(tree, Errors.CantRefBeforeCtorCalled(sym));
+ return;
+ }
+
+ // Field may not have an initializer
+ if ((sym.flags() & HASINIT) != 0) {
+ log.error(tree, Errors.CantAssignInitializedBeforeCtorCalled(sym));
+ return;
+ }
+ }
+ return;
+ }
+ tree = TreeInfo.skipParens(tree);
+ if (sym != null) {
+ if (!sym.isStatic() && sym.kind == VAR && sym.owner.kind == TYP) {
+ if (sym.name == names._this || sym.name == names._super) {
+ // are we seeing something like `this` or `CurrentClass.this` or `SuperClass.super::foo`?
+ if (TreeInfo.isExplicitThisReference(
+ types,
+ (ClassType)localEnv.enclClass.sym.type,
+ tree)) {
+ reportPrologueError(tree, sym);
+ }
+ } else if (sym.kind == VAR && sym.owner.kind == TYP) { // now fields only
+ if (sym.owner != localEnv.enclClass.sym) {
+ if (localEnv.enclClass.sym.isSubClass(sym.owner, types) &&
+ sym.isInheritedIn(localEnv.enclClass.sym, types)) {
+ /* if we are dealing with a field that doesn't belong to the current class, but the
+ * field is inherited, this is an error. Unless, the super class is also an outer
+ * class and the field's qualifier refers to the outer class
+ */
+ if (tree.hasTag(IDENT) ||
+ TreeInfo.isExplicitThisReference(
+ types,
+ (ClassType)localEnv.enclClass.sym.type,
+ ((JCFieldAccess)tree).selected)) {
+ reportPrologueError(tree, sym);
+ }
+ }
+ } else if (isEarlyReference(localEnv, tree, sym)) {
+ /* now this is a `proper` instance field of the current class
+ * references to fields of identity classes which happen to have initializers are
+ * not allowed in the prologue
+ */
+ if (insideLambdaOrClassDef ||
+ (!localEnv.enclClass.sym.isValueClass() && (sym.flags_field & HASINIT) != 0))
+ reportPrologueError(tree, sym);
+ // we will need to generate a proxy for this field later on
+ if (!isInLHS) {
+ if (allowValueClasses) {
+ localProxyVarsGen.addFieldReadInPrologue(localEnv.enclMethod, sym);
+ } else {
+ reportPrologueError(tree, sym);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine if the symbol appearance constitutes an early reference to the current class.
+ *
+ * <p>
+ * This means the symbol is an instance field, or method, of the current class and it appears
+ * in an early initialization context of it (i.e., one of its constructor prologues).
+ *
+ * @param env The current environment
+ * @param tree the AST referencing the variable
+ * @param sym The symbol
+ */
+ private boolean isEarlyReference(Env<AttrContext> env, JCTree tree, Symbol sym) {
+ if ((sym.flags() & STATIC) == 0 &&
+ (sym.kind == VAR || sym.kind == MTH) &&
+ sym.isMemberOf(env.enclClass.sym, types)) {
+ // Allow "Foo.this.x" when "Foo" is (also) an outer class, as this refers to the outer instance
+ if (tree instanceof JCFieldAccess fa) {
+ return TreeInfo.isExplicitThisReference(types, (ClassType)env.enclClass.type, fa.selected);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /* scanner for a select expression, anything that is not a select or identifier
+ * will be stored for further analysis
+ */
+ class SelectScanner extends DeferredAttr.FilterScanner {
+ JCTree scanLater;
+
+ SelectScanner() {
+ super(Set.of(IDENT, SELECT, PARENS));
+ }
+
+ @Override
+ void skip(JCTree tree) {
+ scanLater = tree;
+ }
+ }
+ }
+
public void visitVarDef(JCVariableDecl tree) {
// Local variables have not been entered yet, so we need to do it now:
if (env.info.scope.owner.kind == MTH || env.info.scope.owner.kind == VAR) {
if (tree.sym != null) {
// parameters have already been entered
initEnv.info.lint = lint;
// In order to catch self-references, we set the variable's
// declaration position to maximal possible value, effectively
// marking the variable as undefined.
initEnv.info.enclVar = v;
- attribExpr(tree.init, initEnv, v.type);
- if (tree.isImplicitlyTyped()) {
- //fixup local variable type
- v.type = chk.checkLocalVarType(tree, tree.init.type, tree.name);
+ boolean previousCtorPrologue = initEnv.info.ctorPrologue;
+ try {
+ if (v.owner.kind == TYP && !v.isStatic() && v.isStrict()) {
+ // strict instance initializer in a value class
+ initEnv.info.ctorPrologue = true;
+ }
+ attribExpr(tree.init, initEnv, v.type);
+ if (tree.isImplicitlyTyped()) {
+ //fixup local variable type
+ v.type = chk.checkLocalVarType(tree, tree.init.type, tree.name);
+ }
+ if (v.owner.kind == TYP && !v.isStatic() && v.isStrict()) {
+ // strict field initializers are inlined in constructor's prologues
+ CtorPrologueVisitor ctorPrologueVisitor = new CtorPrologueVisitor(initEnv);
+ ctorPrologueVisitor.scan(tree.init);
+ }
+ } finally {
+ initEnv.info.ctorPrologue = previousCtorPrologue;
}
}
if (tree.isImplicitlyTyped()) {
setSyntheticVariableType(tree, v.type);
}
env.info.scope.owner.flags() & STRICTFP, names.empty, initBlockType,
env.info.scope.owner);
final Env<AttrContext> localEnv =
env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner)));
- if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
+ if ((tree.flags & STATIC) != 0) {
+ localEnv.info.staticLevel++;
+ } else {
+ localEnv.info.instanceInitializerBlock = true;
+ }
// Attribute all type annotations in the block
annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null);
annotate.flush();
attribStats(tree.stats, localEnv);
}
return null;
}
public void visitSynchronized(JCSynchronized tree) {
- chk.checkRefType(tree.pos(), attribExpr(tree.lock, env));
- if (tree.lock.type != null && tree.lock.type.isValueBased()) {
+ boolean identityType = chk.checkIdentityType(tree.pos(), attribExpr(tree.lock, env));
+ if (identityType && tree.lock.type != null && tree.lock.type.isValueBased()) {
env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass);
}
attribStat(tree.body, env);
result = null;
}
skind = KindSelector.of(skind, KindSelector.VAL, KindSelector.TYP);
}
// Attribute the qualifier expression, and determine its symbol (if any).
Type site = attribTree(tree.selected, env, new ResultInfo(skind, Type.noType));
+ Assert.check(site == tree.selected.type);
if (!pkind().contains(KindSelector.TYP_PCK))
site = capture(site); // Capture field access
// don't allow T.class T[].class, etc
if (skind == KindSelector.TYP) {
if (!hasErrorSuper) {
log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.NonSealedWithNoSealedSupertype(c));
}
}
- } else {
+ } else if ((c.flags_field & Flags.COMPOUND) == 0) {
if (c.isDirectlyOrIndirectlyLocal() && !c.isEnum()) {
log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.LocalClassesCantExtendSealed(c.isAnonymous() ? Fragments.Anonymous : Fragments.Local));
}
if (!c.type.isCompound()) {
if (rs.isSerializable(c.type)) {
env.info.isSerializable = true;
}
+ if (c.isValueClass()) {
+ Assert.check(env.tree.hasTag(CLASSDEF));
+ chk.checkConstraintsOfValueClass((JCClassDecl) env.tree, c);
+ }
+
attribClassBody(env, c);
chk.checkDeprecatedAnnotation(env.tree.pos(), c);
chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c);
chk.checkFunctionalInterface((JCClassDecl) env.tree, c);
// Check for proper use of serialVersionUID and other
// serialization-related fields and methods
if (env.info.lint.isEnabled(LintCategory.SERIAL)
&& rs.isSerializable(c.type)
&& !c.isAnonymous()) {
- chk.checkSerialStructure(tree, c);
+ chk.checkSerialStructure(env, tree, c);
}
// Correctly organize the positions of the type annotations
typeAnnotations.organizeTypeAnnotationsBodies(tree);
// Check type annotations applicability rules
< prev index next >