diff --git a/src/java.base/share/classes/java/lang/IdentityObject.java b/src/java.base/share/classes/java/lang/IdentityObject.java index 0508802ae08..4362ed61969 100644 --- a/src/java.base/share/classes/java/lang/IdentityObject.java +++ b/src/java.base/share/classes/java/lang/IdentityObject.java @@ -27,7 +27,39 @@ /** * A restricted interface implemented by all identity objects. - * @since 1.14 -*/ + + * IdentityObject: An object with identity. + * + * *Identity* is a property of certain objects, determined at instance creation + * time and preserved throughout the life of the object. While an object's field + * values may change, its identity is constant. Object identities are unique: no + * two objects created by different instance creation operations can have the same + * identity. + * + * Every object is either an *identity object* or a *primitive object*. Primitive + * objects lack identity. + * + * The following operations have special behavior when applied to identity objects: + * + * - The `==` operator, and the default implementation of the `Object.equals` + * method, compare the identities of their operands, producing `true` for an + * identity object only if the object is being compared to itself. + * + * - The `System.identityHashCode` method, and the default implementation of the + * `Object.hashCode` method, generate a hash code from an identity object's + * identity. + * + * - The `synchronized` modifier and `synchronized` statement are only able to + * successfully acquire a lock when applied to an identity object. + * + * A class may implement `IdentityObject` or `PrimitiveObject`, but never both. + * Primitive classes always implement `PrimitiveObject`, while all other concrete + * classes (except `Object`) implicitly implement `IdentityObject`. + * + * Abstract classes and interfaces may implement or extend this interface if they + * wish to guarantee that all instances of the class or interface have identity. + * + * @since 1.16 + */ public interface IdentityObject { } diff --git a/src/java.base/share/classes/java/lang/PrimitiveObject.java b/src/java.base/share/classes/java/lang/PrimitiveObject.java new file mode 100644 index 00000000000..f737f95d578 --- /dev/null +++ b/src/java.base/share/classes/java/lang/PrimitiveObject.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * A restricted interface implemented by all primitive objects. + * + * A primitive object is an instance of a primitive class, lacking identity. + * + * Every object is either an *identity object* or a *primitive object*. Identity + * objects have a unique identity determined for them at instance creation time and + * preserved throughout their life. + * + * Primitive objects do *not* have an identity. Instead, they simply aggregate a + * set of immutable field values. The lack of identity enables certain performance + * optimizations by Java Virtual Machine implementations. + * The following operations have special behavior when applied to primitive + * objects: + * + * - The `==` operator, and the default implementation of the `Object.equals` + * method, compare the values of the operands' fields. Primitive objects + * created at different points in a program may be `==`. + * + * - The `System.identityHashCode` method, and the default implementation of the + * `Object.hashCode` method, generate a hash code from the hash codes of a + * primitive object's fields. + * + * - The `synchronized` modifier and `synchronized` statement always fail when + * applied to a primitive object. + * + * A class may implement `PrimitiveObject` or `IdentityObject`, but never both. + * Primitive classes always implement `PrimitiveObject`, while all other concrete + * classes (except `Object`) implicitly implement `IdentityObject`. + * + * Abstract classes and interfaces may implement or extend this interface if they + * wish to guarantee that all instances of the class or interface are primitive + * objects. + * + * @since 1.16 + */ + +public interface PrimitiveObject { +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 2f9ff9da7f2..d53fa74d85a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -223,6 +223,7 @@ public static Symtab instance(Context context) { public final Type typeDescriptorType; public final Type recordType; public final Type identityObjectType; + public final Type primitiveObjectType; public final Type valueBasedType; /** The symbol representing the length field of an array. @@ -598,6 +599,7 @@ public R accept(ElementVisitor v, P p) { typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor"); recordType = enterClass("java.lang.Record"); identityObjectType = enterClass("java.lang.IdentityObject"); + primitiveObjectType = enterClass("java.lang.PrimitiveObject"); valueBasedType = enterClass("jdk.internal.ValueBased"); synthesizeEmptyInterfaceIfMissing(autoCloseableType); @@ -607,6 +609,7 @@ public R accept(ElementVisitor v, P p) { synthesizeEmptyInterfaceIfMissing(serializedLambdaType); synthesizeEmptyInterfaceIfMissing(stringConcatFactory); synthesizeEmptyInterfaceIfMissing(identityObjectType); + synthesizeEmptyInterfaceIfMissing(primitiveObjectType); synthesizeBoxTypeIfMissing(doubleType); synthesizeBoxTypeIfMissing(floatType); synthesizeBoxTypeIfMissing(voidType); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 1b887d27864..237b1440ef4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -2241,7 +2241,7 @@ V.ref is a subtype of I. OIOW, whether asSuper(V.ref, sym) != null. (Likewise fo if (!isPrimitiveClass(t)) return syms.objectType; } - if (sym.type == syms.identityObjectType) { + if (sym == syms.identityObjectType.tsym) { // IdentityObject is super interface of every concrete identity class other than jlO if (t.isPrimitiveClass() || t.tsym == syms.objectType.tsym) return null; @@ -2250,6 +2250,12 @@ V.ref is a subtype of I. OIOW, whether asSuper(V.ref, sym) != null. (Likewise fo if (t.hasTag(CLASS) && !t.isReferenceProjection() && !t.tsym.isInterface() && !t.tsym.isAbstract()) { return syms.identityObjectType; } // else fall through and look for explicit coded super interface + } else if (sym == syms.primitiveObjectType.tsym) { + if (t.isPrimitiveClass() || t.isReferenceProjection()) + return syms.primitiveObjectType; + if (t.hasTag(ARRAY) || t.tsym == syms.objectType.tsym) + return null; + // else fall through and look for explicit coded super interface } return asSuper.visit(t, sym); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 24d543a9199..3d365f726e8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -2695,8 +2695,14 @@ void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) { } checkCompatibleConcretes(pos, c); - if (c.isPrimitiveClass() && types.asSuper(c, syms.identityObjectType.tsym, true) != null) { + boolean implementsIdentityObject = types.asSuper(c, syms.identityObjectType.tsym, true) != null; + boolean implementsPrimitiveObject = types.asSuper(c, syms.primitiveObjectType.tsym, true) != null; + if (c.isPrimitiveClass() && implementsIdentityObject) { log.error(pos, Errors.PrimitiveClassMustNotImplementIdentityObject(c)); + } else if (implementsPrimitiveObject && !c.isPrimitiveClass() && !c.isReferenceProjection() && !c.tsym.isInterface() && !c.tsym.isAbstract()) { + log.error(pos, Errors.IdentityClassMustNotImplementPrimitiveObject(c)); + } else if (implementsPrimitiveObject && implementsIdentityObject) { + log.error(pos, Errors.MutuallyIncompatibleSuperInterfaces(c)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index a8acc27d4f4..01d12cc793e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -3820,6 +3820,14 @@ compiler.err.generic.parameterization.with.primitive.class=\ compiler.err.primitive.class.must.not.implement.identity.object=\ The primitive class {0} attempts to implement the incompatible interface IdentityObject +# 0: type +compiler.err.identity.class.must.not.implement.primitive.object=\ + The identity class {0} attempts to implement the incompatible interface PrimitiveObject + +# 0: type +compiler.err.mutually.incompatible.super.interfaces=\ + The type {0} attempts to implement the mutually incompatible interfaces PrimitiveObject and IdentityObject + # 0: symbol, 1: type compiler.err.concrete.supertype.for.primitive.class=\ The concrete class {1} is not allowed to be a super class of the primitive class {0} either directly or indirectly diff --git a/test/langtools/tools/javac/diags/examples/TopInterfaces.java b/test/langtools/tools/javac/diags/examples/TopInterfaces.java new file mode 100644 index 00000000000..a3859cb3c5d --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/TopInterfaces.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.identity.class.must.not.implement.primitive.object +// key: compiler.err.mutually.incompatible.super.interfaces + +class Identity implements PrimitiveObject { + abstract class Inner implements IdentityObject, PrimitiveObject {} +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/InstanceOfTopTypeTest.java b/test/langtools/tools/javac/valhalla/lworld-values/InstanceOfTopTypeTest.java index 543403c79ee..084f31c4d25 100644 --- a/test/langtools/tools/javac/valhalla/lworld-values/InstanceOfTopTypeTest.java +++ b/test/langtools/tools/javac/valhalla/lworld-values/InstanceOfTopTypeTest.java @@ -29,8 +29,7 @@ */ public class InstanceOfTopTypeTest { - interface InlineObject {} - static primitive class V implements InlineObject { + static primitive class V { int x = 42; } @@ -39,28 +38,28 @@ public static void main(String [] args) { Object o = new InstanceOfTopTypeTest(); if (o instanceof IdentityObject) points++; // 1 - if (o instanceof InlineObject) + if (o instanceof PrimitiveObject) throw new AssertionError("Broken"); o = new V(); if (o instanceof IdentityObject) throw new AssertionError("Broken"); - if (o instanceof InlineObject) + if (o instanceof PrimitiveObject) points++; // 2 Object [] oa = new InstanceOfTopTypeTest[] { new InstanceOfTopTypeTest() }; if (oa instanceof IdentityObject) points++; // 3 if (oa[0] instanceof IdentityObject) points++; // 4 - if (oa[0] instanceof InlineObject) + if (oa[0] instanceof PrimitiveObject) throw new AssertionError("Broken"); oa = new V[] { new V() }; if (oa instanceof IdentityObject) points++; // 5 if (oa[0] instanceof IdentityObject) throw new AssertionError("Broken"); - if (oa[0] instanceof InlineObject) + if (oa[0] instanceof PrimitiveObject) points++; - if (points != 6) - throw new AssertionError("Broken top type set up" + points); + if (points != 4) + throw new AssertionError("Broken top type set up " + points); } }