Skip to content
Permalink
Browse files
8242900: [lworld] Allow an inline type to declare a superclass that m…
…eets specified restrictions
  • Loading branch information
Srikanth Adayapalam committed Apr 23, 2020
1 parent d4828d7 commit 21a4dae797e19c56f8b3effe604e4990df54edb7
Show file tree
Hide file tree
Showing 28 changed files with 437 additions and 51 deletions.
@@ -126,6 +126,19 @@ public static EnumSet<Flag> asFlagSet(long flags) {
*/
public static final int HASINIT = 1<<18;

/** Flag is set for a class symbol if it defines one or more non-empty
* instance initializer block(s). This is relevenat only for class symbols
* that originate from source types. For binary types the instance initializer
* blocks are "normalized" into the constructors.
*/
public static final int HASINITBLOCK = 1<<18;

/** Flag is set for a method symbol if it is an empty no-arg ctor.
* i.e one that simply returns (jlO) or merely chains to a super's
* EMPTYNOARGCONSTR
*/
public static final int EMPTYNOARGCONSTR = 1<<18;

/** Flag is set for a value based class.
*/
public static final int VALUEBASED = 1<<19;
@@ -458,6 +471,8 @@ public enum Flag {
ANNOTATION(Flags.ANNOTATION),
DEPRECATED(Flags.DEPRECATED),
HASINIT(Flags.HASINIT),
HASINITBLOCK(Flags.HASINITBLOCK),
EMPTYNOARGCONSTR(Flags.EMPTYNOARGCONSTR),
BLOCK(Flags.BLOCK),
ENUM(Flags.ENUM),
MANDATED(Flags.MANDATED),
@@ -1194,7 +1194,7 @@ public void visitMethodDef(JCMethodDecl tree) {
if (tree.name == names.init && owner.type != syms.objectType) {
JCBlock body = tree.body;
if (body.stats.isEmpty() ||
TreeInfo.getConstructorInvocationName(body.stats, names) == names.empty) {
TreeInfo.getConstructorInvocationName(body.stats, names, true) == names.empty) {
JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(),
make.Ident(names._super), make.Idents(List.nil())));
body.stats = body.stats.prepend(supCall);
@@ -1230,6 +1230,12 @@ public void visitMethodDef(JCMethodDecl tree) {
}
}
}
if (m.isConstructor() && m.type.getParameterTypes().size() == 0) {
if ((owner.type == syms.objectType) ||
(tree.body.stats.size() == 1 && TreeInfo.getConstructorInvocationName(tree.body.stats, names, false) == names._super)) {
m.flags_field |= EMPTYNOARGCONSTR;
}
}

// Attribute all type annotations in the body
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
@@ -1427,7 +1433,11 @@ public void visitBlock(JCBlock tree) {
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 if (tree.stats.size() > 0)
env.info.scope.owner.flags_field |= HASINITBLOCK;

// Attribute all type annotations in the block
annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null);
annotate.flush();
@@ -5203,6 +5213,14 @@ void attribClass(ClassSymbol c) throws CompletionFailure {

attribClassBody(env, c);

if ((c.flags() & (VALUE | ABSTRACT)) == VALUE) { // for non-intersection, concrete values.
Assert.check(env.tree.hasTag(CLASSDEF));
JCClassDecl classDecl = (JCClassDecl) env.tree;
if (classDecl.extending != null) {
chk.checkConstraintsOfInlineSuper(env.tree.pos(), c);
}
}

chk.checkDeprecatedAnnotation(env.tree.pos(), c);
chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c);
chk.checkFunctionalInterface((JCClassDecl) env.tree, c);
@@ -743,6 +743,50 @@ private Object asTypeParam(Type t) {
: t;
}

void checkConstraintsOfInlineSuper(DiagnosticPosition pos, ClassSymbol c) {
boolean indirectSuper = false;
for(Type st = types.supertype(c.type); st != Type.noType; indirectSuper = true, st = types.supertype(st)) {
if (st == null || st.tsym == null || st.tsym.kind == ERR)
return;
if (indirectSuper && st.tsym == syms.objectType.tsym)
return;
if (!st.tsym.isAbstract()) {
log.error(pos, Errors.ConcreteSupertypeForInlineClass(c, st));
}
if ((st.tsym.flags() & HASINITBLOCK) != 0) {
log.error(pos, Errors.SuperClassDeclaresInitBlock(c, st));
}
// No instance fields and no arged constructors both mean inner classes cannot be inline supers.
Type encl = st.getEnclosingType();
if (encl != null && encl.hasTag(CLASS)) {
log.error(pos, Errors.SuperClassCannotBeInner(c, st));
}
for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
switch (s.kind) {
case VAR:
if ((s.flags() & STATIC) == 0) {
log.error(pos, Errors.SuperFieldNotAllowed(s, c, st));
}
break;
case MTH:
if ((s.flags() & SYNCHRONIZED) != 0) {
log.error(pos, Errors.SuperMethodCannotBeSynchronized(s, c, st));
} else if (s.isConstructor()) {
MethodSymbol m = (MethodSymbol)s;
if (m.getParameters().size() > 0) {
log.error(pos, Errors.SuperConstructorCannotTakeArguments(m, c, st));
} else {
if ((m.flags() & (GENERATEDCONSTR | EMPTYNOARGCONSTR)) == 0) {
log.error(pos, Errors.SuperNoArgConstructorMustBeEmpty(m, c, st));
}
}
}
break;
}
}
}
}

/** Check that type is a valid qualifier for a constructor reference expression
*/
Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
@@ -688,8 +688,6 @@ protected void attribSuperTypes(Env<AttrContext> env, Env<AttrContext> baseEnv)
final boolean isValueType = (tree.mods.flags & Flags.VALUE) != 0;

if (tree.extending != null) {
if (isValueType)
log.error(tree.pos(), Errors.ValueMayNotExtend);
extending = clearTypeParams(tree.extending);
supertype = attr.attribBase(extending, baseEnv, true, false, true);
if (supertype == syms.recordType) {
@@ -710,7 +708,7 @@ protected void attribSuperTypes(Env<AttrContext> env, Env<AttrContext> baseEnv)
if (injectTopInterfaceTypes) {
if (isValueType || types.isValue(supertype)) {
interfaceToInject = syms.inlineObjectType;
} else if ((sym.flags_field & INTERFACE) == 0) { // skip interfaces and annotations.
} else if ((sym.flags_field & (INTERFACE | ABSTRACT)) == 0) { // skip interfaces, abstract classes and annotations.
if (sym.fullname != names.java_lang_Object) {
interfaceToInject = syms.identityObjectType;
}
@@ -792,6 +792,17 @@ private void initAttributeReaders() {

new AttributeReader(names.Code, V45_3, MEMBER_ATTRIBUTE) {
protected void read(Symbol sym, int attrLen) {
if (allowInlineTypes) {
if (sym.isConstructor() && ((MethodSymbol) sym).type.getParameterTypes().size() == 0) {
int code_length = buf.getInt(bp + 4);
if ((code_length == 1 && buf.getByte( bp + 8) == (byte) ByteCodes.return_) ||
(code_length == 5 && buf.getByte(bp + 8) == ByteCodes.aload_0 &&
buf.getByte( bp + 9) == (byte) ByteCodes.invokespecial &&
buf.getByte( bp + 12) == (byte) ByteCodes.return_)) {
sym.flags_field |= EMPTYNOARGCONSTR;
}
}
}
if (saveParameterNames)
((MethodSymbol)sym).code = readCode(sym);
else
@@ -194,7 +194,6 @@ public void visitMethodDef(JCMethodDecl tree) {
MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth);
if (names._super.equals(name)) { // "initial" constructor.
// Synthesize code to allocate factory "product" via: V $this = V.default;
Assert.check(symbol.owner == syms.objectType.tsym);
Assert.check(symbol.type.getParameterTypes().size() == 0);
final JCExpression type = make.Type(currentClass.type);
rhs = make.Select(type, new VarSymbol(STATIC, names._default, currentClass.type, currentClass.sym));
@@ -3715,3 +3715,33 @@ compiler.err.identity.type.must.not.implement.inline.object=\
compiler.err.mutually.incompatible.interfaces=\
The interfaces IdentityObject and InlineObject are mutually exclusive. The type {0} cannot implement both

# 0: symbol, 1: type
compiler.err.concrete.supertype.for.inline.class=\
The concrete class {1} is not allowed to be a super class of the inline class {0} either directly or indirectly

# 0: symbol, 1: symbol, 2: type
compiler.err.super.method.cannot.be.synchronized=\
The method {0} in the super class {2} of the inline type {1} is synchronized. This is disallowed

# 0: symbol, 1: symbol, 2: type
compiler.err.super.constructor.cannot.take.arguments=\
The super class {2} of the inline type {1} defines a constructor {0} that takes arguments. This is disallowed

# 0: symbol, 1: symbol, 2: type
compiler.err.super.field.not.allowed=\
The super class {2} of the inline type {1} defines an instance field {0}. This is disallowed

# 0: symbol, 1: symbol, 2: type
compiler.err.super.no.arg.constructor.must.be.empty=\
The super class {2} of the inline type {1} defines a nonempty no-arg constructor {0}. This is disallowed

# 0: symbol, 1: type
compiler.err.super.class.declares.init.block=\
The super class {1} of the inline class {0} declares one or more non-empty instance initializer blocks. This is disallowed.

# 0: symbol, 1: type
compiler.err.super.class.cannot.be.inner=\
The super class {1} of the inline class {0} is an inner class. This is disallowed.



@@ -109,17 +109,20 @@ public static boolean hasConstructors(List<JCTree> trees) {
}

/** Is there a constructor invocation in the given list of trees?
* Optionally, check only for no-arg ctor invocation
*/
public static Name getConstructorInvocationName(List<? extends JCTree> trees, Names names) {
public static Name getConstructorInvocationName(List<? extends JCTree> trees, Names names, boolean argsAllowed) {
for (JCTree tree : trees) {
if (tree.hasTag(EXEC)) {
JCExpressionStatement stat = (JCExpressionStatement)tree;
if (stat.expr.hasTag(APPLY)) {
JCMethodInvocation apply = (JCMethodInvocation)stat.expr;
Name methName = TreeInfo.name(apply.meth);
if (methName == names._this ||
methName == names._super) {
return methName;
if (argsAllowed || apply.args.size() == 0) {
Name methName = TreeInfo.name(apply.meth);
if (methName == names._this ||
methName == names._super) {
return methName;
}
}
}
}
@@ -74,9 +74,8 @@ private static void testSuper() throws Exception {
private static void testInterfaces() throws Exception {
AnnotatedType[] as;
as = TestClassArray.class.getAnnotatedInterfaces();
check(as.length == 4); // Adjust as per JDK-8237952
check(as.length == 3);
check(as[1].getAnnotations().length == 0);
check(as[3].getAnnotations().length == 0); // Adjust as per JDK-8237952

Annotation[] annos;
annos = as[0].getAnnotations();
@@ -327,9 +326,8 @@ private static void testParameterizedType() {
// Base
AnnotatedType[] as;
as = TestParameterizedType.class.getAnnotatedInterfaces();
check(as.length == 2); // Adjust as per JDK-8237952
check(as.length == 1);
check(as[0].getAnnotations().length == 1);
check(as[1].getAnnotations().length == 0); // Adjust as per JDK-8237952
check(as[0].getAnnotation(TypeAnno.class).value().equals("M"));

Annotation[] annos;
@@ -80,9 +80,8 @@ static void testSuperInterfaces() {
System.out.println("testing superinterfaces");
Type[] sis = cls.getGenericInterfaces();
assert
(sis.length == 1) : // Adjust based on JDK-8237952
"C1 should have one generic superinterface"; // Adjust based on JDK-8237952
assert (sis[0] == IdentityObject.class); // Adjust based on JDK-8237952
(sis.length == 0) :
"C1 should have no generic superinterfaces";
}

static void testTypeParameters() {
@@ -183,8 +183,8 @@ static void testSuperInterfaces() {
System.out.println("testing superinterfaces");
Type[] sis = cls.getGenericInterfaces();
assert
((sis.length == 4)): // Adjust based on JDK-8237952
"C2 should have four generic superinterfaces"; // Adjust based on JDK-8237952
((sis.length == 3)):
"C2 should have three generic superinterfaces";

Type t = sis[0];
assert
@@ -217,8 +217,6 @@ static void testSuperInterfaces() {
t == I3.class :
"Third superinterface of C2 is I3";

assert (sis[3] == IdentityObject.class); // Adjust based on JDK-8237952

// Test interfaces themselves

TypeVariable[] tvs = I1.class.getTypeParameters();
@@ -211,3 +211,10 @@ compiler.warn.this.exposed.prematurely
compiler.err.identity.type.must.not.implement.inline.object
compiler.err.inline.type.must.not.implement.identity.object
compiler.err.mutually.incompatible.interfaces
compiler.err.concrete.supertype.for.inline.class
compiler.err.super.class.cannot.be.inner
compiler.err.super.class.declares.init.block
compiler.err.super.constructor.cannot.take.arguments
compiler.err.super.field.not.allowed
compiler.err.super.method.cannot.be.synchronized
compiler.err.super.no.arg.constructor.must.be.empty
@@ -22,15 +22,15 @@ public class GeneratedClass<T> extends java.util.ArrayList<java.lang.String> imp
round: 2

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedSource();

public void test(long a);
}

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedClass();

@@ -39,15 +39,15 @@ public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.N
round: 3

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedSource();

public void test(long a);
}

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedClass();

@@ -1,15 +1,15 @@
round: 1

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedSource<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedSource();

public void test(long a);
}

@javax.annotation.processing.SupportedAnnotationTypes({"*"})
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence, java.lang.IdentityObject {
public abstract class GeneratedClass<E> extends java.util.LinkedList<java.lang.Number> implements java.lang.Runnable, java.lang.CharSequence {

public GeneratedClass();

0 comments on commit 21a4dae

Please sign in to comment.