Skip to content
This repository has been archived by the owner on Aug 27, 2022. It is now read-only.

Commit

Permalink
8242478: compiler implementation for records (Second Preview)
Browse files Browse the repository at this point in the history
Reviewed-by: mcimadamore, jlahoda, darcy
  • Loading branch information
Vicente Romero committed May 17, 2020
1 parent a2057ad commit 9efdaac
Show file tree
Hide file tree
Showing 31 changed files with 833 additions and 182 deletions.
2 changes: 1 addition & 1 deletion src/java.base/share/classes/java/io/ObjectStreamClass.java
Expand Up @@ -1585,7 +1585,7 @@ private static MethodHandle canonicalRecordCtr(Class<?> cls) {
.map(RecordComponent::getType)
.toArray(Class<?>[]::new);
try {
Constructor<?> ctr = cls.getConstructor(paramTypes);
Constructor<?> ctr = cls.getDeclaredConstructor(paramTypes);
ctr.setAccessible(true);
return MethodHandles.lookup().unreflectConstructor(ctr);
} catch (IllegalAccessException | NoSuchMethodException e) {
Expand Down
Expand Up @@ -371,7 +371,7 @@ public static EnumSet<Flag> asFlagSet(long flags) {
public static final int
AccessFlags = PUBLIC | PROTECTED | PRIVATE,
LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC,
LocalRecordFlags = LocalClassFlags | STATIC,
StaticLocalFlags = LocalClassFlags | STATIC | INTERFACE | ANNOTATION,
MemberClassFlags = LocalClassFlags | INTERFACE | AccessFlags,
MemberRecordFlags = MemberClassFlags | STATIC,
ClassFlags = LocalClassFlags | INTERFACE | PUBLIC | ANNOTATION,
Expand Down
Expand Up @@ -1491,7 +1491,10 @@ public RecordComponent getRecordComponent(VarSymbol field) {

public RecordComponent getRecordComponent(JCVariableDecl var, boolean addIfMissing, List<JCAnnotation> annotations) {
for (RecordComponent rc : recordComponents) {
if (rc.name == var.name) {
/* it could be that a record erroneously declares two record components with the same name, in that
* case we need to use the position to disambiguate
*/
if (rc.name == var.name && var.pos == rc.pos) {
return rc;
}
}
Expand Down Expand Up @@ -1753,18 +1756,29 @@ public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
public static class RecordComponent extends VarSymbol implements RecordComponentElement {
public MethodSymbol accessor;
public JCTree.JCMethodDecl accessorMeth;
/* the original annotations applied to the record component
*/
private final List<JCAnnotation> originalAnnos;
/* if the user happens to erroneously declare two components with the same name, we need a way to differentiate
* them, the code will fail anyway but we need to keep the information for better error recovery
*/
private final int pos;

/**
* Construct a record component, given its flags, name, type and owner.
*/
public RecordComponent(JCVariableDecl fieldDecl, List<JCAnnotation> annotations) {
super(PUBLIC, fieldDecl.sym.name, fieldDecl.sym.type, fieldDecl.sym.owner);
this.originalAnnos = annotations;
this.pos = fieldDecl.pos;
}

public List<JCAnnotation> getOriginalAnnos() { return originalAnnos; }

public boolean isVarargs() {
return type.hasTag(TypeTag.ARRAY) && ((ArrayType)type).isVarargs();
}

@Override @DefinedBy(Api.LANGUAGE_MODEL)
@SuppressWarnings("preview")
public ElementKind getKind() {
Expand Down
47 changes: 34 additions & 13 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Expand Up @@ -274,7 +274,7 @@ boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
Symbol owner = env.info.scope.owner;
// owner refers to the innermost variable, method or
// initializer block declaration at this point.
return
boolean isAssignable =
v.owner == owner
||
((owner.name == names.init || // i.e. we are in a constructor
Expand All @@ -284,6 +284,8 @@ boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
v.owner == owner.owner
&&
((v.flags() & STATIC) != 0) == Resolve.isStatic(env));
boolean insideCompactConstructor = env.enclMethod != null && TreeInfo.isCompactConstructor(env.enclMethod);
return isAssignable & !insideCompactConstructor;
}

/** Check that variable can be assigned to.
Expand Down Expand Up @@ -1078,35 +1080,59 @@ public void visitMethodDef(JCMethodDecl tree) {
} else {
// but if it is the canonical:

// if user generated, then it shouldn't explicitly invoke any other constructor
/* if user generated, then it shouldn't:
* - have an accessibility stricter than that of the record type
* - explicitly invoke any other constructor
*/
if ((tree.sym.flags_field & GENERATEDCONSTR) == 0) {
if (Check.protection(m.flags()) > Check.protection(env.enclClass.sym.flags())) {
log.error(tree,
(env.enclClass.sym.flags() & AccessFlags) == 0 ?
Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical,
env.enclClass.sym.name,
Fragments.CanonicalMustNotHaveStrongerAccess("package")
) :
Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical,
env.enclClass.sym.name,
Fragments.CanonicalMustNotHaveStrongerAccess(asFlagSet(env.enclClass.sym.flags() & AccessFlags))
)
);
}

JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
if (app != null &&
(TreeInfo.name(app.meth) == names._this ||
TreeInfo.name(app.meth) == names._super) &&
checkFirstConstructorStat(app, tree, false)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, tree.sym.name,
Fragments.Canonical, env.enclClass.sym.name,
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
}
}

// also we want to check that no type variables have been defined
if (!tree.typarams.isEmpty()) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, tree.sym.name, Fragments.CanonicalMustNotDeclareTypeVariables));
Fragments.Canonical, env.enclClass.sym.name, Fragments.CanonicalMustNotDeclareTypeVariables));
}

/* and now we need to check that the constructor's arguments are exactly the same as those of the
* record components
*/
List<Type> recordComponentTypes = TreeInfo.recordFields(env.enclClass).map(vd -> vd.sym.type);
List<? extends RecordComponent> recordComponents = env.enclClass.sym.getRecordComponents();
List<Type> recordFieldTypes = TreeInfo.recordFields(env.enclClass).map(vd -> vd.sym.type);
for (JCVariableDecl param: tree.params) {
if (!types.isSameType(param.type, recordComponentTypes.head)) {
boolean paramIsVarArgs = (param.sym.flags_field & VARARGS) != 0;
if (!types.isSameType(param.type, recordFieldTypes.head) ||
(recordComponents.head.isVarargs() != paramIsVarArgs)) {
log.error(param, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, tree.sym.name, Fragments.TypeMustBeIdenticalToCorrespondingRecordComponentType));
Fragments.Canonical, env.enclClass.sym.name,
Fragments.TypeMustBeIdenticalToCorrespondingRecordComponentType));
}
recordComponentTypes = recordComponentTypes.tail;
recordComponents = recordComponents.tail;
recordFieldTypes = recordFieldTypes.tail;
}
}
}
Expand Down Expand Up @@ -1180,11 +1206,6 @@ public void visitMethodDef(JCMethodDecl tree) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name, Fragments.CanonicalWithNameMismatch));
}
if (!tree.sym.isPublic()) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
TreeInfo.isCompactConstructor(tree) ? Fragments.Compact : Fragments.Canonical,
env.enclClass.sym.name, Fragments.CanonicalConstructorMustBePublic));
}
if (tree.sym.type.asMethodType().thrown != null && !tree.sym.type.asMethodType().thrown.isEmpty()) {
log.error(tree,
Errors.InvalidCanonicalConstructorInRecord(
Expand Down
35 changes: 21 additions & 14 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
Expand Up @@ -1211,26 +1211,23 @@ else if ((sym.owner.flags_field & INTERFACE) != 0)
break;
case TYP:
if (sym.isLocal()) {
mask = (flags & RECORD) != 0 ? LocalRecordFlags : LocalClassFlags;
if ((flags & RECORD) != 0) {
implicit = STATIC;
boolean implicitlyStatic = !sym.isAnonymous() &&
((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
mask = staticOrImplicitlyStatic && allowRecords ? StaticLocalFlags : LocalClassFlags;
implicit = implicitlyStatic ? STATIC : implicit;
if (staticOrImplicitlyStatic) {
if (sym.owner.kind == TYP) {
log.error(pos, Errors.RecordDeclarationNotAllowedInInnerClasses);
log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
}
}
if ((sym.owner.flags_field & STATIC) == 0 &&
(flags & ENUM) != 0) {
log.error(pos, Errors.EnumsMustBeStatic);
}
} else if (sym.owner.kind == TYP) {
mask = (flags & RECORD) != 0 ? MemberRecordFlags : MemberClassFlags;
if (sym.owner.owner.kind == PCK ||
(sym.owner.flags_field & STATIC) != 0)
mask |= STATIC;
else if ((flags & ENUM) != 0) {
log.error(pos, Errors.EnumsMustBeStatic);
} else if ((flags & RECORD) != 0) {
log.error(pos, Errors.RecordDeclarationNotAllowedInInnerClasses);
else if ((flags & ENUM) != 0 || (flags & RECORD) != 0) {
log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
}
// Nested interfaces and enums are always STATIC (Spec ???)
if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
Expand Down Expand Up @@ -1264,7 +1261,7 @@ else if ((flags & ENUM) != 0) {
}
else {
log.error(pos,
Errors.ModNotAllowedHere(asFlagSet(illegal)));
Errors.ModNotAllowedHere(asFlagSet(illegal)));
}
}
else if ((sym.kind == TYP ||
Expand Down Expand Up @@ -2070,11 +2067,21 @@ boolean checkCommonOverriderIn(Symbol s1, Symbol s2, Type site) {
*/
void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
ClassSymbol origin = (ClassSymbol)m.owner;
if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name))
if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
log.error(tree.pos(), Errors.EnumNoFinalize);
return;
}
}
if (allowRecords && origin.isRecord()) {
// let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
.filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
if (recordComponent.isPresent()) {
return;
}
}

for (Type t = origin.type; t.hasTag(CLASS);
t = types.supertype(t)) {
if (t != origin.type) {
Expand Down
Expand Up @@ -57,7 +57,6 @@
import com.sun.tools.javac.resources.CompilerProperties.Fragments;

import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.code.TypeTag.BOT;
import static com.sun.tools.javac.tree.JCTree.Tag.*;

import com.sun.tools.javac.util.Dependencies.CompletionCause;
Expand Down Expand Up @@ -1025,7 +1024,6 @@ void finishClass(JCClassDecl tree, Env<AttrContext> env) {
List<JCTree> defsToEnter = isRecord ?
tree.defs.diff(alreadyEntered) : tree.defs;
memberEnter.memberEnter(defsToEnter, env);
List<JCTree> defsBeforeAddingNewMembers = tree.defs;
if (isRecord) {
addRecordMembersIfNeeded(tree, env);
}
Expand All @@ -1048,7 +1046,7 @@ private void addAccessor(JCVariableDecl tree, Env<AttrContext> env) {
new TreeCopier<JCTree>(make.at(tree.pos)).copy(rec.getOriginalAnnos());
JCMethodDecl getter = make.at(tree.pos).
MethodDef(
make.Modifiers(Flags.PUBLIC | Flags.GENERATED_MEMBER, originalAnnos),
make.Modifiers(PUBLIC | Flags.GENERATED_MEMBER, originalAnnos),
tree.sym.name,
/* we need to special case for the case when the user declared the type as an ident
* if we don't do that then we can have issues if type annotations are applied to the
Expand Down Expand Up @@ -1123,7 +1121,7 @@ JCMethodDecl getCanonicalConstructorDecl(JCClassDecl tree) {
private void addRecordMembersIfNeeded(JCClassDecl tree, Env<AttrContext> env) {
if (lookupMethod(tree.sym, names.toString, List.nil()) == null) {
JCMethodDecl toString = make.
MethodDef(make.Modifiers(Flags.PUBLIC | Flags.RECORD | Flags.GENERATED_MEMBER),
MethodDef(make.Modifiers(Flags.PUBLIC | Flags.RECORD | Flags.FINAL | Flags.GENERATED_MEMBER),
names.toString,
make.Type(syms.stringType),
List.nil(),
Expand Down Expand Up @@ -1223,9 +1221,6 @@ public MethodSymbol constructorSymbol() {
(types.supertype(owner().type).tsym == syms.enumSym)) {
// constructors of true enums are private
flags = PRIVATE | GENERATEDCONSTR;
} else if ((owner().flags_field & RECORD) != 0) {
// record constructors are public
flags = PUBLIC | GENERATEDCONSTR;
} else {
flags = (owner().flags() & AccessFlags) | GENERATEDCONSTR;
}
Expand Down Expand Up @@ -1313,21 +1308,25 @@ public List<Name> superArgs() {
}

class RecordConstructorHelper extends BasicConstructorHelper {

List<VarSymbol> recordFieldSymbols;
boolean lastIsVarargs;
List<JCVariableDecl> recordFieldDecls;

RecordConstructorHelper(TypeSymbol owner, List<JCVariableDecl> recordFieldDecls) {
RecordConstructorHelper(ClassSymbol owner, List<JCVariableDecl> recordFieldDecls) {
super(owner);
this.recordFieldDecls = recordFieldDecls;
this.recordFieldSymbols = recordFieldDecls.map(vd -> vd.sym);
this.lastIsVarargs = owner.getRecordComponents().stream().anyMatch(rc -> rc.isVarargs());
}

@Override
public Type constructorType() {
if (constructorType == null) {
List<Type> argtypes = recordFieldSymbols.map(v -> (v.flags_field & Flags.VARARGS) != 0 ? types.elemtype(v.type) : v.type);
constructorType = new MethodType(argtypes, syms.voidType, List.nil(), syms.methodClass);
ListBuffer<Type> argtypes = new ListBuffer<>();
JCVariableDecl lastField = recordFieldDecls.last();
for (JCVariableDecl field : recordFieldDecls) {
argtypes.add(field == lastField && lastIsVarargs ? types.elemtype(field.sym.type) : field.sym.type);
}

constructorType = new MethodType(argtypes.toList(), syms.voidType, List.nil(), syms.methodClass);
}
return constructorType;
}
Expand All @@ -1340,11 +1339,14 @@ public MethodSymbol constructorSymbol() {
*/
csym.flags_field |= Flags.COMPACT_RECORD_CONSTRUCTOR | GENERATEDCONSTR;
ListBuffer<VarSymbol> params = new ListBuffer<>();
for (VarSymbol p : recordFieldSymbols) {
params.add(new VarSymbol(GENERATED_MEMBER | PARAMETER | RECORD | ((p.flags_field & Flags.VARARGS) != 0 ? Flags.VARARGS : 0), p.name, p.type, csym));
JCVariableDecl lastField = recordFieldDecls.last();
for (JCVariableDecl field : recordFieldDecls) {
params.add(new VarSymbol(
GENERATED_MEMBER | PARAMETER | RECORD | (field == lastField && lastIsVarargs ? Flags.VARARGS : 0),
field.name, field.sym.type, csym));
}
csym.params = params.toList();
csym.flags_field |= RECORD | PUBLIC;
csym.flags_field |= RECORD;
return csym;
}

Expand Down

0 comments on commit 9efdaac

Please sign in to comment.