changeset 47868:04260073c6bd datum

datum: code dump of current development
author vromero
date Tue, 07 Nov 2017 11:10:12 -0500
parents 5c760bbf3863
children 4f946e7de3a8
files src/java.base/share/classes/java/lang/DataClass.java src/java.base/share/classes/java/lang/annotation/Data.java src/java.base/share/classes/java/lang/invoke/ObjectMethodBuilders.java src/java.compiler/share/classes/javax/lang/model/util/Elements.java src/jdk.compiler/share/classes/com/sun/source/doctree/AccessorTree.java src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Accessors.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Tokens.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/CommentUtils.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/MemberSummaryBuilder.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/AccessorTaglet.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberMap.java src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java test/langtools/tools/javac/datum/CheckDatumMembersAccess.java test/langtools/tools/javac/datum/DataClassAsSuper.java test/langtools/tools/javac/datum/DataClassAsSuper.out test/langtools/tools/javac/datum/DataClassTest.java test/langtools/tools/javac/datum/DatumCanNotDeclaredFieldsWithSameName.java test/langtools/tools/javac/datum/DatumCanNotDeclaredFieldsWithSameName.out test/langtools/tools/javac/datum/DatumShouldDeclareAtLeastOneFieldTest.java test/langtools/tools/javac/datum/DatumShouldDeclareAtLeastOneFieldTest.out test/langtools/tools/javac/datum/Neg01.java test/langtools/tools/javac/datum/Neg01.out test/langtools/tools/javac/datum/Pos01.java test/langtools/tools/javac/datum/Pos02.java test/langtools/tools/javac/datum/SubDatumCannotPassDuplicateArgsToSuperTest.java test/langtools/tools/javac/datum/SubDatumCannotPassDuplicateArgsToSuperTest.out test/langtools/tools/javac/datum/SubDatumFieldsMustBeAPrefixOfParentTest.java test/langtools/tools/javac/datum/SubDatumFieldsMustBeAPrefixOfParentTest.out
diffstat 66 files changed, 2417 insertions(+), 181 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/DataClass.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, 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;
+
+import java.lang.annotation.Data;
+
+/**
+ * DataClass
+ *
+ * @author Brian Goetz
+ */
+@Data
+public abstract class DataClass {
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object obj);
+
+    @Override
+    public abstract String toString();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/annotation/Data.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, 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.annotation;
+
+/**
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Data {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/ObjectMethodBuilders.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2017, 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.invoke;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * ObjectMethodBuilders
+ *
+ * @author Brian Goetz
+ */
+public class ObjectMethodBuilders {
+    private static final MethodType DESCRIPTOR_MT = MethodType.methodType(MethodType.class);
+    private static final MethodType NAMES_MT = MethodType.methodType(List.class);
+    private static final MethodHandle FALSE = MethodHandles.constant(boolean.class, false);
+    private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true);
+    private static final MethodHandle ZERO = MethodHandles.constant(int.class, 0);
+    private static final MethodHandle CLASS_IS_INSTANCE;
+    private static final MethodHandle OBJECT_EQUALS;
+    private static final MethodHandle OBJECTS_EQUALS;
+    private static final MethodHandle OBJECTS_HASHCODE;
+    private static final MethodHandle OBJECTS_TOSTRING;
+    private static final MethodHandle OBJECT_EQ;
+    private static final MethodHandle OBJECT_HASHCODE;
+    private static final MethodHandle OBJECT_TO_STRING;
+    private static final MethodHandle STRING_FORMAT;
+    private static final MethodHandle HASH_COMBINER;
+
+    private static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
+    private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
+    private static final HashMap<Class<?>, MethodHandle> primitiveToString = new HashMap<>();
+
+    static {
+        try {
+            MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+            MethodHandles.Lookup lookup = MethodHandles.Lookup.IMPL_LOOKUP;
+
+            CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance", MethodType.methodType(boolean.class, Object.class));
+            OBJECT_EQUALS = publicLookup.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class));
+            OBJECT_HASHCODE = publicLookup.findVirtual(Object.class, "hashCode", MethodType.fromMethodDescriptorString("()I", null));
+            OBJECT_TO_STRING = publicLookup.findVirtual(Object.class, "toString", MethodType.methodType(String.class));
+            STRING_FORMAT = publicLookup.findStatic(String.class, "format", MethodType.methodType(String.class, String.class, Object[].class));
+            OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals", MethodType.methodType(boolean.class, Object.class, Object.class));
+            OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode", MethodType.methodType(int.class, Object.class));
+            OBJECTS_TOSTRING = publicLookup.findStatic(Objects.class, "toString", MethodType.methodType(String.class, Object.class));
+
+            OBJECT_EQ = lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.methodType(boolean.class, Object.class, Object.class));
+            HASH_COMBINER = lookup.findStatic(ObjectMethodBuilders.class, "hashCombiner", MethodType.fromMethodDescriptorString("(II)I", null));
+            primitiveEquals.put(byte.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(BB)Z", null)));
+            primitiveEquals.put(short.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(SS)Z", null)));
+            primitiveEquals.put(char.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(CC)Z", null)));
+            primitiveEquals.put(int.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(II)Z", null)));
+            primitiveEquals.put(long.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(JJ)Z", null)));
+            primitiveEquals.put(float.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(FF)Z", null)));
+            primitiveEquals.put(double.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(DD)Z", null)));
+            primitiveEquals.put(boolean.class, lookup.findStatic(ObjectMethodBuilders.class, "eq", MethodType.fromMethodDescriptorString("(ZZ)Z", null)));
+
+            primitiveHashers.put(byte.class, lookup.findStatic(Byte.class, "hashCode", MethodType.fromMethodDescriptorString("(B)I", null)));
+            primitiveHashers.put(short.class, lookup.findStatic(Short.class, "hashCode", MethodType.fromMethodDescriptorString("(S)I", null)));
+            primitiveHashers.put(char.class, lookup.findStatic(Character.class, "hashCode", MethodType.fromMethodDescriptorString("(C)I", null)));
+            primitiveHashers.put(int.class, lookup.findStatic(Integer.class, "hashCode", MethodType.fromMethodDescriptorString("(I)I", null)));
+            primitiveHashers.put(long.class, lookup.findStatic(Long.class, "hashCode", MethodType.fromMethodDescriptorString("(J)I", null)));
+            primitiveHashers.put(float.class, lookup.findStatic(Float.class, "hashCode", MethodType.fromMethodDescriptorString("(F)I", null)));
+            primitiveHashers.put(double.class, lookup.findStatic(Double.class, "hashCode", MethodType.fromMethodDescriptorString("(D)I", null)));
+            primitiveHashers.put(boolean.class, lookup.findStatic(Boolean.class, "hashCode", MethodType.fromMethodDescriptorString("(Z)I", null)));
+
+            primitiveToString.put(byte.class, lookup.findStatic(Byte.class, "toString", MethodType.methodType(String.class, byte.class)));
+            primitiveToString.put(short.class, lookup.findStatic(Short.class, "toString", MethodType.methodType(String.class, short.class)));
+            primitiveToString.put(char.class, lookup.findStatic(Character.class, "toString", MethodType.methodType(String.class, char.class)));
+            primitiveToString.put(int.class, lookup.findStatic(Integer.class, "toString", MethodType.methodType(String.class, int.class)));
+            primitiveToString.put(long.class, lookup.findStatic(Long.class, "toString", MethodType.methodType(String.class, long.class)));
+            primitiveToString.put(float.class, lookup.findStatic(Float.class, "toString", MethodType.methodType(String.class, float.class)));
+            primitiveToString.put(double.class, lookup.findStatic(Double.class, "toString", MethodType.methodType(String.class, double.class)));
+            primitiveToString.put(boolean.class, lookup.findStatic(Boolean.class, "toString", MethodType.methodType(String.class, boolean.class)));
+        }
+        catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static int hashCombiner(int x, int y) {
+        return x*31 + y;
+    }
+
+    private static boolean eq(Object a, Object b) { return a == b; }
+    private static boolean eq(byte a, byte b) { return a == b; }
+    private static boolean eq(short a, short b) { return a == b; }
+    private static boolean eq(char a, char b) { return a == b; }
+    private static boolean eq(int a, int b) { return a == b; }
+    private static boolean eq(long a, long b) { return a == b; }
+    private static boolean eq(float a, float b) { return a == b; }
+    private static boolean eq(double a, double b) { return a == b; }
+    private static boolean eq(boolean a, boolean b) { return a == b; }
+
+    /** Get the method handle for combining two values of a given type */
+    private static MethodHandle equalator(Class<?> clazz) {
+        return (clazz.isPrimitive()
+                ? primitiveEquals.get(clazz)
+                : OBJECTS_EQUALS.asType(MethodType.methodType(boolean.class, clazz, clazz)));
+    }
+
+    /** Get the hasher for a value of a given type */
+    private static MethodHandle hasher(Class<?> clazz) {
+        return (clazz.isPrimitive()
+                ? primitiveHashers.get(clazz)
+                : OBJECTS_HASHCODE.asType(MethodType.methodType(int.class, clazz)));
+    }
+
+    /** Get the stringifier for a value of a given type */
+    private static MethodHandle stringifier(Class<?> clazz) {
+        return (clazz.isPrimitive()
+                ? primitiveToString.get(clazz)
+                : OBJECTS_TOSTRING.asType(MethodType.methodType(String.class, clazz)));
+    }
+
+    /**
+     * Generates a method handle for the {@code equals} method for a given data class
+     * @param receiverClass   the data class
+     * @param getters         the list of getters
+     * @return the method handle
+     */
+    private static MethodHandle makeEquals(Class<?> receiverClass,
+                                          List<MethodHandle> getters) {
+        MethodType rr = MethodType.methodType(boolean.class, receiverClass, receiverClass);
+        MethodType ro = MethodType.methodType(boolean.class, receiverClass, Object.class);
+        MethodHandle instanceFalse = MethodHandles.dropArguments(FALSE, 0, receiverClass, Object.class); // (RO)Z
+        MethodHandle instanceTrue = MethodHandles.dropArguments(TRUE, 0, receiverClass, Object.class); // (RO)Z
+        MethodHandle isSameObject = OBJECT_EQ.asType(ro); // (RO)Z
+        MethodHandle isInstance = MethodHandles.dropArguments(CLASS_IS_INSTANCE.bindTo(receiverClass), 0, receiverClass); // (RO)Z
+        MethodHandle accumulator = MethodHandles.dropArguments(TRUE, 0, receiverClass, receiverClass); // (RR)Z
+
+        for (MethodHandle getter : getters) {
+            MethodHandle equalator = equalator(getter.type().returnType()); // (TT)Z
+            MethodHandle thisFieldEqual = MethodHandles.filterArguments(equalator, 0, getter, getter); // (RR)Z
+            accumulator = MethodHandles.guardWithTest(thisFieldEqual, accumulator, instanceFalse.asType(rr));
+        }
+
+        return MethodHandles.guardWithTest(isSameObject,
+                                           instanceTrue,
+                                           MethodHandles.guardWithTest(isInstance, accumulator.asType(ro), instanceFalse));
+    }
+
+    /**
+     * Generates a method handle for the {@code hashCode} method for a given data class
+     * @param receiverClass   the data class
+     * @param getters         the list of getters
+     * @return the method handle
+     */
+    private static MethodHandle makeHashCode(Class<?> receiverClass,
+                                            List<MethodHandle> getters) {
+        MethodHandle accumulator = MethodHandles.dropArguments(ZERO, 0, receiverClass); // (R)I
+
+        // @@@ Use loop combinator instead?
+        for (MethodHandle getter : getters) {
+            MethodHandle hasher = hasher(getter.type().returnType()); // (T)I
+            MethodHandle hashThisField = MethodHandles.filterArguments(hasher, 0, getter);    // (R)I
+            MethodHandle combineHashes = MethodHandles.filterArguments(HASH_COMBINER, 0, accumulator, hashThisField); // (RR)I
+            accumulator = MethodHandles.permuteArguments(combineHashes, accumulator.type(), 0, 0); // adapt (R)I to (RR)I
+        }
+
+        return accumulator;
+    }
+
+    /**
+     * Generates a method handle for the {@code toString} method for a given data class
+     * @param receiverClass   the data class
+     * @param getters         the list of getters
+     * @param names           the names
+     * @return the method handle
+     */
+    private static MethodHandle makeToString(Class<?> receiverClass,
+                                            List<MethodHandle> getters,
+                                            List<String> names) {
+        // This is a pretty lousy algorithm; we spread the receiver over N places,
+        // apply the N getters, apply N toString operations, and concat the result with String.format
+        // Better to use String.format directly, or delegate to StringConcatFactory
+        // Also probably want some quoting around String components
+
+        assert getters.size() == names.size();
+
+        int[] invArgs = new int[getters.size()];
+        Arrays.fill(invArgs, 0);
+        MethodHandle[] filters = new MethodHandle[getters.size()];
+        StringBuilder sb = new StringBuilder();
+        sb.append(receiverClass.getSimpleName()).append("[");
+        for (int i=0; i<getters.size(); i++) {
+            MethodHandle getter = getters.get(i); // (R)T
+            MethodHandle stringify = stringifier(getter.type().returnType()); // (T)String
+            MethodHandle stringifyThisField = MethodHandles.filterArguments(stringify, 0, getter);    // (R)String
+            filters[i] = stringifyThisField;
+            sb.append(names.get(i)).append("=%s");
+            if (i != getters.size() - 1)
+                sb.append(", ");
+        }
+        sb.append(']');
+        String formatString = sb.toString();
+        MethodHandle formatter = MethodHandles.insertArguments(STRING_FORMAT, 0, formatString)
+                                              .asCollector(String[].class, getters.size()); // (R*)String
+        if (getters.size() == 0) {
+            // Add back extra R
+            formatter = MethodHandles.dropArguments(formatter, 0, receiverClass);
+        }
+        else {
+            MethodHandle filtered = MethodHandles.filterArguments(formatter, 0, filters);
+            formatter = MethodHandles.permuteArguments(filtered, MethodType.methodType(String.class, receiverClass), invArgs);
+        }
+
+        return formatter;
+    }
+
+    /**
+     * Bootstrap method to generate the {@code equals} method for a given data class
+     * @param lookup    the lookup
+     * @param invName   the name
+     * @param invType   the method type
+     * @param dataClass the data class
+     * @param getters   the list of getters
+     * @return a call site
+     * @throws Throwable if any exception is thrown during call site construction
+     */
+    public static CallSite makeEquals(MethodHandles.Lookup lookup, String invName, MethodType invType,
+                                      Class<?> dataClass, MethodHandle... getters) throws Throwable {
+        return new ConstantCallSite(makeEquals(dataClass, List.of(getters)));
+    }
+
+    /**
+     * Bootstrap method to generate the {@code hashCode} method for a given data class
+     * @param lookup    the lookup
+     * @param invName   the name
+     * @param invType   the method type
+     * @param dataClass the data class
+     * @param getters   the list of getters
+     * @return a call site
+     * @throws Throwable if any exception is thrown during call site construction
+     */
+    public static CallSite makeHashCode(MethodHandles.Lookup lookup, String invName, MethodType invType,
+                                        Class<?> dataClass, MethodHandle... getters) throws Throwable {
+        return new ConstantCallSite(makeHashCode(dataClass, List.of(getters)));
+    }
+
+    /**
+     * Bootstrap method to generate the {@code toString} method for a given data class
+     * @param lookup    the lookup
+     * @param invName   the name
+     * @param invType   the method type
+     * @param dataClass the data class
+     * @param names     the list of field names joined into a string, separated by ";"
+     * @param getters   the list of getters
+     * @return a call site
+     * @throws Throwable if any exception is thrown during call site construction
+     */
+    public static CallSite makeToString(MethodHandles.Lookup lookup, String invName, MethodType invType,
+                                        Class<?> dataClass, String names, MethodHandle... getters) throws Throwable {
+        return new ConstantCallSite(makeToString(dataClass, List.of(getters), List.of(names.split(";"))));
+    }
+}
--- a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java	Tue Nov 07 11:10:12 2017 -0500
@@ -612,4 +612,18 @@
      * @since 1.8
      */
     boolean isFunctionalInterface(TypeElement type);
+
+    /**
+     * Returns the executable element for the getter associated with the given variable element.
+     * @param variableElement the field for which the getter is to be found.
+     * @return the field's getter.
+     */
+    ExecutableElement getterFor(VariableElement variableElement);
+
+    /**
+     * Returns the executable element for the setter associated with the given variable element.
+     * @param variableElement the field for which the setter is to be found.
+     * @return the field's setter.
+     */
+    ExecutableElement setterFor(VariableElement variableElement);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/AccessorTree.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 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 com.sun.source.doctree;
+
+import java.util.List;
+
+/**
+ *
+ * A tree node for an @getter or @setter block tag.
+ *
+ * <p>
+ * &#064;getter description <br>
+ * &#064;setter description <br>
+ *
+ * @since 1.10
+ */
+public interface AccessorTree extends BlockTagTree {
+    /**
+     * Returns the description of the {@code @getter} or {@code @setter} tag.
+     * @return the description associated with this tag
+     */
+    List<? extends DocTree> getDescription();
+}
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java	Tue Nov 07 11:10:12 2017 -0500
@@ -35,6 +35,19 @@
      * Enumerates all kinds of trees.
      */
     enum Kind {
+
+        /**
+         * Used for instances of {@link AccessorTree}
+         * representing an embedded getter JavaDoc.
+         */
+        GETTER("getter"),
+
+        /**
+         * Used for instances of {@link AccessorTree}
+         * representing an embedded getter JavaDoc.
+         */
+        SETTER("setter"),
+
         /**
          * Used for instances of {@link AttributeTree}
          * representing an HTML attribute.
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java	Tue Nov 07 11:10:12 2017 -0500
@@ -57,6 +57,14 @@
 public interface DocTreeVisitor<R,P> {
 
     /**
+     * Visits an AaccessorTree node.
+     * @param node the node being visited
+     * @param p a parameter value
+     * @return a result value
+     */
+    R visitAccessor(AccessorTree node, P p);
+
+    /**
      * Visits an AttributeTree node.
      * @param node the node being visited
      * @param p a parameter value
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java	Tue Nov 07 11:10:12 2017 -0500
@@ -31,6 +31,7 @@
 import javax.tools.Diagnostic;
 import javax.tools.JavaFileObject;
 
+import com.sun.source.doctree.AccessorTree;
 import com.sun.source.doctree.AttributeTree;
 import com.sun.source.doctree.AttributeTree.ValueKind;
 import com.sun.source.doctree.AuthorTree;
@@ -39,6 +40,7 @@
 import com.sun.source.doctree.DocCommentTree;
 import com.sun.source.doctree.DocRootTree;
 import com.sun.source.doctree.DocTree;
+import com.sun.source.doctree.DocTree.Kind;
 import com.sun.source.doctree.EndElementTree;
 import com.sun.source.doctree.EntityTree;
 import com.sun.source.doctree.ErroneousTree;
@@ -211,6 +213,13 @@
     LiteralTree newLiteralTree(TextTree text);
 
     /**
+     * Create a new {@code AccessorTree} object, to represent a {@code @getter} tag.
+     * @param description the content of the tag
+     * @return a {@code AccessorTree} object
+     */
+    AccessorTree newAccessorTree(Kind kind, List<? extends DocTree> description);
+
+    /**
      * Create a new {@code ParamTree} object, to represent a {@code @param } tag.
      * @param isTypeParameter true if this is a type parameter, and false otherwise
      * @param name the parameter being described
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java	Tue Nov 07 11:10:12 2017 -0500
@@ -464,6 +464,18 @@
     }
 
     /**
+     * {@inheritDoc} This implementation returns {@code null}.
+     *
+     * @param node  {@inheritDoc}
+     * @param p  {@inheritDoc}
+     * @return the result of scanning
+     */
+    @Override
+    public R visitAccessor(AccessorTree node, P p) {
+        return scan(node.getDescription(), p);
+    }
+
+    /**
      * {@inheritDoc} This implementation scans the children in left to right order.
      *
      * @param node  {@inheritDoc}
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java	Tue Nov 07 11:10:12 2017 -0500
@@ -283,6 +283,18 @@
      * @return  the result of {@code defaultAction}
      */
     @Override
+    public R visitAccessor(AccessorTree node, P p) {
+        return defaultAction(node, p);
+    }
+
+    /**
+     * {@inheritDoc} This implementation calls {@code defaultAction}.
+     *
+     * @param node {@inheritDoc}
+     * @param p {@inheritDoc}
+     * @return  the result of {@code defaultAction}
+     */
+    @Override
     public R visitParam(ParamTree node, P p) {
         return defaultAction(node, p);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Accessors.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, 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 com.sun.tools.javac.code;
+
+import com.sun.tools.javac.code.Type.MethodType;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+
+import java.util.function.Function;
+
+public class Accessors {
+
+    public enum Kind {
+        GET(names -> names.get) {
+            @Override
+            public Type accessorType(Symtab syms, Type type) {
+                return new MethodType(List.nil(), type, List.nil(), syms.methodClass);
+            }
+        },
+        SET(names -> names.set) {
+            @Override
+            public Type accessorType(Symtab syms, Type type) {
+                return new MethodType(List.of(type), syms.voidType, List.nil(), syms.methodClass);
+            }
+        };
+
+        Function<Names, Name> nameFunc;
+
+        Kind(Function<Names, Name> nameFunc) {
+            this.nameFunc = nameFunc;
+        }
+
+        public Name name(Names names) {
+            return nameFunc.apply(names);
+        }
+
+        public abstract Type accessorType(Symtab syms, Type type);
+    }
+
+    public static Kind fromName(Name name) {
+        for (Kind k : Kind.values()) {
+            if (k.name(name.table.names).equals(name)) {
+                return k;
+            }
+        }
+        return null;
+    }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Tue Nov 07 11:10:12 2017 -0500
@@ -313,6 +313,17 @@
      */
     public static final long ANONCONSTR_BASED = 1L<<57;
 
+    /**
+     * Flag to indicate that a class is a datum. The flag is also used to mark fields that are
+     * part of the state vector of a datum class.
+     */
+    public static final long DATUM = 1L<<58;
+
+    /**
+     * Flag to indicate that a datum field is non-final.
+     */
+    public static final long NON_FINAL = 1L<<59;
+
     /** Modifier masks.
      */
     public static final int
@@ -427,7 +438,9 @@
         SYSTEM_MODULE(Flags.SYSTEM_MODULE),
         DEPRECATED_ANNOTATION(Flags.DEPRECATED_ANNOTATION),
         DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL),
-        HAS_RESOURCE(Flags.HAS_RESOURCE);
+        HAS_RESOURCE(Flags.HAS_RESOURCE),
+        DATUM(Flags.DATUM),
+        NON_FINAL(Flags.NON_FINAL);
 
         Flag(long flag) {
             this.value = flag;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Nov 07 11:10:12 2017 -0500
@@ -361,6 +361,10 @@
         return (flags_field & DEPRECATED) != 0;
     }
 
+    public boolean isDatum() {
+        return (flags_field & DATUM) != 0;
+    }
+
     public boolean hasDeprecatedAnnotation() {
         return (flags_field & DEPRECATED_ANNOTATION) != 0;
     }
@@ -393,6 +397,10 @@
         return (flags() & INTERFACE) != 0;
     }
 
+    public boolean isAbstract() {
+        return (flags() & ABSTRACT) != 0;
+    }
+
     public boolean isPrivate() {
         return (flags_field & Flags.AccessFlags) == PRIVATE;
     }
@@ -1501,6 +1509,8 @@
          */
         public int adr = -1;
 
+        public List<Pair<Accessors.Kind, MethodSymbol>> accessors = List.nil();
+
         /** Construct a variable symbol, given its flags, name, type and owner.
          */
         public VarSymbol(long flags, Name name, Type type, Symbol owner) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Nov 07 11:10:12 2017 -0500
@@ -158,6 +158,9 @@
     /** Predefined types.
      */
     public final Type objectType;
+    public final Type dataClassType;
+    public final Type dataAnnotationType;
+    public final Type objectMethodBuildersType;
     public final Type objectsType;
     public final Type classType;
     public final Type classLoaderType;
@@ -482,6 +485,9 @@
 
         // Enter predefined classes. All are assumed to be in the java.base module.
         objectType = enterClass("java.lang.Object");
+        dataClassType = enterClass("java.lang.DataClass");
+        dataAnnotationType = enterClass("java.lang.annotation.Data");
+        objectMethodBuildersType = enterClass("java.lang.invoke.ObjectMethodBuilders");
         objectsType = enterClass("java.util.Objects");
         classType = enterClass("java.lang.Class");
         stringType = enterClass("java.lang.String");
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Tue Nov 07 11:10:12 2017 -0500
@@ -1482,6 +1482,19 @@
 
     // </editor-fold>
 
+    public List<VarSymbol> datumVars(Type t) {
+        List<VarSymbol> vars = List.nil();
+        while (!t.hasTag(NONE)) {
+            if (t.hasTag(CLASS)) {
+                for (Symbol s : t.tsym.members().getSymbols(s -> s.kind == VAR && (s.flags() & DATUM) != 0)) {
+                    vars = vars.prepend((VarSymbol)s);
+                }
+            }
+            t = supertype(t);
+        }
+        return vars;
+    }
+
     // <editor-fold defaultstate="collapsed" desc="Contains Type">
     public boolean containedBy(Type t, Type s) {
         switch (t.getTag()) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Nov 07 11:10:12 2017 -0500
@@ -26,6 +26,7 @@
 package com.sun.tools.javac.comp;
 
 import java.util.*;
+import java.util.stream.Collectors;
 
 import javax.lang.model.element.ElementKind;
 import javax.tools.JavaFileObject;
@@ -1070,11 +1071,9 @@
                     JCBlock body = tree.body;
                     if (body.stats.isEmpty() ||
                             !TreeInfo.isSelfCall(body.stats.head)) {
-                        body.stats = body.stats.
-                                prepend(typeEnter.SuperCall(make.at(body.pos),
-                                        List.nil(),
-                                        List.nil(),
-                                        false));
+                        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);
                     } else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
                             (tree.mods.flags & GENERATEDCONSTR) == 0 &&
                             TreeInfo.isSuperCall(body.stats.head)) {
@@ -4612,6 +4611,44 @@
                     env.info.isSerializable = true;
                 }
 
+                if ((c.flags() & DATUM) != 0) {
+                    Type sup = types.supertype(c.type);
+                    List<JCVariableDecl> superFields = TreeInfo.superDatumFields(env.enclClass);
+                    if (sup.tsym != syms.dataClassType.tsym &&
+                            (sup.tsym.flags() & (ABSTRACT | DATUM)) != (ABSTRACT | DATUM)) {
+                        log.error(env.enclClass.extending.pos(), Errors.CantExtendDatum(Fragments.BadDatumSuper));
+                    }
+
+                    if (superFields.nonEmpty()) {
+                        if (c.members().findFirst(names.init, s -> (s.flags() & DATUM) == 0) != null) {
+                            log.error(env.enclClass.extending.pos(), Errors.CantExtendDatum(Fragments.BadSuperFields));
+                        }
+                    }
+
+                    List<VarSymbol> supDatumFields = types.datumVars(sup);
+                    for (JCTree supField : superFields) {
+                        JCVariableDecl supVarDecl = (JCVariableDecl)supField;
+                        if (supDatumFields.isEmpty()) break; //arity mismatches will be checked inside implicit constructor
+                        if (supDatumFields.head.name != supVarDecl.name ||
+                                !types.isSameType(supDatumFields.head.type, supVarDecl.vartype.type)) {
+                            log.error(env.enclClass.extending.pos(),
+                                    Errors.CantExtendDatum(
+                                            Fragments.SuperFieldMismatch(
+                                                    supDatumFields.head.type, supDatumFields.head.name,
+                                                    supVarDecl.vartype.type, supVarDecl.name)));
+                            break;
+                        }
+                        supDatumFields = supDatumFields.tail;
+                    }
+
+                    List<VarSymbol> vars = types.datumVars(c.type).stream()
+                            .filter(v -> v.owner == c)
+                            .collect(List.collector());
+                    env.info.datumImplicitConstructor = new MethodSymbol(0, names.init,
+                            new MethodType(vars.map(v -> v.type), syms.voidType, List.nil(), syms.methodClass),
+                            c);
+                }
+
                 attribClassBody(env, c);
 
                 chk.checkDeprecatedAnnotation(env.tree.pos(), c);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java	Tue Nov 07 11:10:12 2017 -0500
@@ -25,6 +25,7 @@
 
 package com.sun.tools.javac.comp;
 
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.code.*;
@@ -112,6 +113,8 @@
      */
     JCTree preferredTreeForDiagnostics;
 
+    MethodSymbol datumImplicitConstructor;
+
     /** Duplicate this context, replacing scope field and copying all others.
      */
     AttrContext dup(WriteableScope scope) {
@@ -132,6 +135,7 @@
         info.isNewClass = isNewClass;
         info.preferredTreeForDiagnostics = preferredTreeForDiagnostics;
         info.visitingServiceImplementation = visitingServiceImplementation;
+        info.datumImplicitConstructor = datumImplicitConstructor;
         return info;
     }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Tue Nov 07 11:10:12 2017 -0500
@@ -28,6 +28,7 @@
 package com.sun.tools.javac.comp;
 
 import java.util.HashMap;
+import java.util.stream.Collectors;
 
 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
 import com.sun.tools.javac.code.*;
@@ -1885,17 +1886,21 @@
                     if (isInitialConstructor) {
                         boolean isSynthesized = (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) {
-                                // choose the diagnostic position based on whether
-                                // the ctor is default(synthesized) or not
-                                if (isSynthesized) {
-                                    checkInit(TreeInfo.diagnosticPositionFor(var, vardecl),
-                                        var, "var.not.initialized.in.default.constructor");
-                                } else {
-                                    checkInit(TreeInfo.diagEndPos(tree.body), var);
+                        boolean isDatum = (tree.sym.owner.flags() & Flags.DATUM) != 0;
+                        // skip datum as they are generated by the compiler and guaranteed to be correct
+                        if (!isDatum || !isSynthesized) {
+                            for (int i = firstadr; i < nextadr; i++) {
+                                JCVariableDecl vardecl = vardecls[i];
+                                VarSymbol var = vardecl.sym;
+                                if (var.owner == classDef.sym) {
+                                    // choose the diagnostic position based on whether
+                                    // the ctor is default(synthesized) or not
+                                    if (isSynthesized) {
+                                        checkInit(TreeInfo.diagnosticPositionFor(var, vardecl),
+                                            var, "var.not.initialized.in.default.constructor");
+                                    } else {
+                                        checkInit(TreeInfo.diagEndPos(tree.body), var);
+                                    }
                                 }
                             }
                         }
@@ -2329,6 +2334,11 @@
         public void visitApply(JCMethodInvocation tree) {
             scanExpr(tree.meth);
             scanExprs(tree.args);
+            if (TreeInfo.name(tree.meth) == names._default) {
+                types.datumVars(classDef.type).stream()
+                        .filter(v -> v.owner == classDef.sym)
+                        .forEach(v -> letInit(tree.pos(), v));
+            }
         }
 
         public void visitNewClass(JCNewClass tree) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Nov 07 11:10:12 2017 -0500
@@ -26,6 +26,7 @@
 package com.sun.tools.javac.comp;
 
 import java.util.*;
+import java.util.stream.Collectors;
 
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Kinds.KindSelector;
@@ -121,6 +122,24 @@
         Options options = Options.instance(context);
         debugLower = options.isSet("debuglower");
         pkginfoOpt = PkgInfo.get(options);
+        String generationSwitch = options.get("generation_switch");
+        if (generationSwitch != null) {
+            if (generationSwitch.equals("all")) {
+                generationSwitchSet = EnumSet.allOf(GenerationSwitch.class);
+            } else {
+                String[] switches = generationSwitch.split(",");
+                for (String s :switches) {
+                    s = s.toUpperCase();
+                    if (s.equals("OLDEQUALS")) {
+                        generationSwitchSet.add(GenerationSwitch.OLD_EQUALS);
+                    } else if (s.equals("OLDTOSTRING")) {
+                        generationSwitchSet.add(GenerationSwitch.OLD_TOSTRING);
+                    } else if (s.equals("OLDHASHCODE")) {
+                        generationSwitchSet.add(GenerationSwitch.OLD_HASCODE);
+                    }
+                }
+            }
+        }
     }
 
     /** The currently enclosing class.
@@ -2221,6 +2240,10 @@
             (types.supertype(currentClass.type).tsym.flags() & ENUM) == 0)
             visitEnumDef(tree);
 
+        if ((tree.mods.flags & (DATUM | ABSTRACT)) == DATUM) {
+            visitDatumDef(tree);
+        }
+
         // If this is a nested class, define a this$n field for
         // it and add to proxies.
         JCVariableDecl otdef = null;
@@ -2290,6 +2313,37 @@
         result = make_at(tree.pos()).Block(SYNTHETIC, List.nil());
     }
 
+    List<JCTree> accessors(JCClassDecl tree) {
+        ListBuffer<JCTree> buffer = new ListBuffer<>();
+        tree.defs.stream()
+                    .filter(t -> t.hasTag(VARDEF))
+                    .map(t -> (JCVariableDecl)t)
+                    .filter(vd -> vd.sym.accessors.nonEmpty())
+                    .forEach(vd -> {
+                        for (Pair<Accessors.Kind, MethodSymbol> accessor : vd.sym.accessors) {
+                            MethodSymbol accessorSym = accessor.snd;
+                            if ((accessorSym.flags() & Flags.MANDATED) != 0) {
+                                make_at(tree.pos());
+                                switch (accessor.fst) {
+                                    case GET:
+                                        buffer.add(make.MethodDef(accessorSym, make.Block(0,
+                                                List.of(make.Return(make.Ident(vd.sym))))));
+                                        break;
+                                    case SET:
+                                        buffer.add(make.MethodDef(accessorSym, make.Block(0,
+                                                List.of(make.Exec(
+                                                        make.Assign(make.Ident(vd.sym), make.Ident(accessorSym.params.head))
+                                                                .setType(vd.sym.type))))));
+                                        break;
+                                    default:
+                                        Assert.error("Cannot get here!");
+                                }
+                            }
+                        }
+                    });
+        return buffer.toList();
+    }
+
     /** Translate an enum class. */
     private void visitEnumDef(JCClassDecl tree) {
         make_at(tree.pos());
@@ -2445,6 +2499,280 @@
             prepend(makeLit(syms.stringType, var.name.toString()));
     }
 
+    /** Translate a datum class. */
+
+    enum GenerationSwitch {
+        OLD_EQUALS,
+        OLD_HASCODE,
+        OLD_TOSTRING
+    }
+
+    private EnumSet<GenerationSwitch> generationSwitchSet = EnumSet.noneOf(GenerationSwitch.class);
+
+    private void visitDatumDef(JCClassDecl tree) {
+        make_at(tree.pos());
+        List<VarSymbol> vars = types.datumVars(tree.type);
+        Pool.MethodHandle[] getterMethHandles = new Pool.MethodHandle[vars.size()];
+        int index = 0;
+        for (VarSymbol var : vars) {
+            if (var.owner != tree.sym) {
+                var = new VarSymbol(var.flags_field, var.name, var.type, tree.sym);
+            }
+            getterMethHandles[index] = new Pool.MethodHandle(ClassFile.REF_getField, var, types);
+            index++;
+        }
+
+        tree.defs = tree.defs.appendList(accessors(tree));
+        tree.defs = tree.defs.appendList(List.of(
+                generationSwitchSet.contains(GenerationSwitch.OLD_EQUALS) ?
+                        datumOldEquals(tree, vars):
+                        datumEquals(tree, getterMethHandles),
+                generationSwitchSet.contains(GenerationSwitch.OLD_TOSTRING) ?
+                        datumOldToString(tree, vars):
+                        datumToString(tree, vars, getterMethHandles),
+                generationSwitchSet.contains(GenerationSwitch.OLD_HASCODE) ?
+                        datumOldHashCode(tree, vars):
+                        datumHashCode(tree, getterMethHandles)
+        ));
+    }
+
+    JCTree datumToString(JCClassDecl tree, List<VarSymbol> vars, Pool.MethodHandle[] getterMethHandles) {
+        make_at(tree.pos());
+
+        MethodSymbol msym = lookupMethod(tree.pos(),
+                         names.toString,
+                         tree.sym.type,
+                         List.nil());
+        if ((msym.flags() & DATUM) != 0) {
+            Name bootstrapName = names.makeToString;
+            Object[] staticArgsValues = new Object[2 + getterMethHandles.length];
+            staticArgsValues[0] = tree.sym;
+            String concatNames = vars.stream()
+                    .map(v -> v.name)
+                    .collect(Collectors.joining(";", "", ""));
+            staticArgsValues[1] = concatNames;
+            int index = 2;
+            for (Object mho : getterMethHandles) {
+                staticArgsValues[index] = mho;
+                index++;
+            }
+
+            List<Type> staticArgTypes = List.of(syms.classType,
+                    syms.stringType,
+                    new ArrayType(syms.methodHandleType, syms.arrayClass));
+
+            JCFieldAccess qualifier = makeIndyQualifier(
+                    tree, msym, staticArgTypes, staticArgsValues, bootstrapName);
+
+            VarSymbol _this = new VarSymbol(SYNTHETIC, names._this, tree.sym.type, tree.sym);
+
+            JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, List.of(make.Ident(_this)));
+            proxyCall.type = qualifier.type;
+            return make.MethodDef(msym, make.Block(0, List.of(make.Return(proxyCall))));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
+    JCTree datumHashCode(JCClassDecl tree, Pool.MethodHandle[] getterMethHandles) {
+        make_at(tree.pos());
+        MethodSymbol msym = lookupMethod(tree.pos(),
+                         names.hashCode,
+                         tree.sym.type,
+                         List.nil());
+        if ((msym.flags() & DATUM) != 0) {
+            Name bootstrapName = names.makeHashCode;
+            Object[] staticArgsValues = new Object[1 + getterMethHandles.length];
+            staticArgsValues[0] = tree.sym;
+            int index = 1;
+            for (Object mho : getterMethHandles) {
+                staticArgsValues[index] = mho;
+                index++;
+            }
+
+            List<Type> staticArgTypes = List.of(syms.classType,
+                    new ArrayType(syms.methodHandleType, syms.arrayClass));
+
+            JCFieldAccess qualifier = makeIndyQualifier(
+                    tree, msym, staticArgTypes, staticArgsValues, bootstrapName);
+
+            VarSymbol _this = new VarSymbol(SYNTHETIC, names._this, tree.sym.type, tree.sym);
+
+            JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, List.of(make.Ident(_this)));
+            proxyCall.type = qualifier.type;
+            return make.MethodDef(msym, make.Block(0, List.of(make.Return(proxyCall))));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
+    JCTree datumEquals(JCClassDecl tree, Pool.MethodHandle[] getterMethHandles) {
+        make_at(tree.pos());
+        MethodSymbol msym = lookupMethod(tree.pos(),
+                         names.equals,
+                         tree.sym.type,
+                         List.of(syms.objectType));
+
+        if ((msym.flags() & DATUM) != 0) {
+            Name bootstrapName = names.makeEquals;
+            Object[] staticArgsValues = new Object[1 + getterMethHandles.length];
+            staticArgsValues[0] = tree.sym;
+            int index = 1;
+            for (Object mho : getterMethHandles) {
+                staticArgsValues[index] = mho;
+                index++;
+            }
+
+            List<Type> staticArgTypes = List.of(syms.classType,
+                    new ArrayType(syms.methodHandleType, syms.arrayClass));
+
+            JCFieldAccess qualifier = makeIndyQualifier(
+                    tree, msym, staticArgTypes, staticArgsValues, bootstrapName);
+
+            VarSymbol o = msym.params.head;
+            o.adr = 0;
+            VarSymbol _this = new VarSymbol(SYNTHETIC, names._this, tree.sym.type, tree.sym);
+
+            JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, List.of(make.Ident(_this), make.Ident(o)));
+            proxyCall.type = qualifier.type;
+            return make.MethodDef(msym, make.Block(0, List.of(make.Return(proxyCall))));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
+    JCFieldAccess makeIndyQualifier(
+            JCClassDecl tree,
+            MethodSymbol msym,
+            List<Type> staticArgTypes,
+            Object[] staticArgValues,
+            Name bootstrapName) {
+        List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
+                syms.stringType,
+                syms.methodTypeType).appendList(staticArgTypes);
+
+        Symbol bsm = rs.resolveInternalMethod(tree.pos(), attrEnv, syms.objectMethodBuildersType,
+                bootstrapName, bsm_staticArgs, List.nil());
+
+        MethodType indyType = msym.type.asMethodType();
+        indyType = new MethodType(
+                indyType.argtypes.prepend(tree.sym.type),
+                indyType.restype,
+                indyType.thrown,
+                syms.methodClass
+        );
+        DynamicMethodSymbol dynSym = new DynamicMethodSymbol(bootstrapName,
+                syms.noSymbol,
+                ClassFile.REF_invokeStatic,
+                (MethodSymbol)bsm,
+                indyType,
+                staticArgValues);
+        JCFieldAccess qualifier = make.Select(make.QualIdent(syms.objectMethodBuildersType.tsym), bootstrapName);
+        qualifier.sym = dynSym;
+        qualifier.type = msym.type.asMethodType().restype;
+        return qualifier;
+    }
+
+    JCTree datumOldToString(JCClassDecl tree, List<VarSymbol> vars) {
+        make_at(tree.pos());
+
+        MethodSymbol toStringSym = lookupMethod(tree.pos(),
+                         names.toString,
+                         tree.sym.type,
+                         List.nil());
+        if ((toStringSym.flags() & DATUM) != 0) {
+            String format = vars.stream()
+                    .map(v -> v.name + "=%s")
+                    .collect(Collectors.joining(", ", tree.name + "[", "]"));
+            JCExpression formatLit = make.Literal(format);
+            JCFieldAccess meth = make.Select(make.Type(syms.stringType), names.fromString("format"));
+            meth.sym = lookupMethod(tree.pos(),
+                    meth.name,
+                    syms.stringType,
+                    List.of(syms.stringType, types.makeArrayType(syms.objectType)));
+            meth.type = meth.sym.type;
+            JCMethodInvocation app = make.Apply(List.nil(), meth,
+                    List.of(formatLit).appendList(vars.map(make::Ident)));
+            app.type = meth.type.getReturnType();
+            app.varargsElement = syms.objectType;
+            return make.MethodDef(toStringSym, make.Block(0, List.of(make.Return(app))));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
+    JCTree datumOldHashCode(JCClassDecl tree, List<VarSymbol> vars) {
+        make_at(tree.pos());
+
+        MethodSymbol hashCodeSym = lookupMethod(tree.pos(),
+                         names.hashCode,
+                         tree.sym.type,
+                         List.nil());
+        if ((hashCodeSym.flags() & DATUM) != 0) {
+            JCFieldAccess meth = make.Select(make.Type(syms.objectsType), names.fromString("hash"));
+            meth.sym = lookupMethod(tree.pos(),
+                    meth.name,
+                    syms.objectsType,
+                    List.of(types.makeArrayType(syms.objectType)));
+            meth.type = meth.sym.type;
+            JCMethodInvocation app = make.Apply(List.nil(), meth, vars.map(make::Ident));
+            app.type = meth.type.getReturnType();
+            app.varargsElement = syms.objectType;
+            return make.MethodDef(hashCodeSym, make.Block(0, List.of(make.Return(app))));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
+    JCTree datumOldEquals(JCClassDecl tree, List<VarSymbol> vars) {
+        make_at(tree.pos());
+
+        MethodSymbol oldEqualsSym = lookupMethod(tree.pos(),
+                         names.equals,
+                         tree.sym.type,
+                         List.of(syms.objectType));
+
+        if ((oldEqualsSym.flags() & DATUM) != 0) {
+            ListBuffer<JCStatement> trueStats = new ListBuffer<>();
+
+            VarSymbol o = oldEqualsSym.params.head;
+
+            VarSymbol that = new VarSymbol(SYNTHETIC, names.fromString("that" + target.syntheticNameChar()),
+                                                types.erasure(tree.type),
+                                                oldEqualsSym);
+
+            trueStats.add(make.VarDef(that,
+                    make.TypeCast(make.Type(types.erasure(tree.type)),
+                            make.Ident(o)).setType(types.erasure(tree.type))));
+
+            Symbol objectEqualsSym = lookupMethod(tree.pos(),
+                    names.equals,
+                    syms.objectsType,
+                    List.of(syms.objectType, syms.objectType));
+            for (VarSymbol v : vars) {
+                JCFieldAccess meth = make.Select(make.Type(syms.objectsType), names.equals);
+                meth.sym = objectEqualsSym;
+                meth.type = meth.sym.type;
+                JCExpression sel = make.Select(make.Ident(that), v);
+                JCMethodInvocation app = make.Apply(List.nil(), meth, List.of(make.Ident(v), sel));
+                app.type = meth.type.getReturnType();
+                JCUnary neg = make.Unary(Tag.NOT, app);
+                neg.operator = operators.resolveUnary(tree.pos(), Tag.NOT, syms.booleanType);
+                neg.type = neg.operator.getReturnType();
+                trueStats.add(make.If(neg, make.Return(make.Literal(false)), null));
+            }
+            trueStats.add(make.Return(make.Literal(true)));
+
+            JCStatement ifStat = make.If(make.TypeTest(make.Ident(o), make.Type(tree.type)).setType(syms.booleanType),
+                    make.Block(0, trueStats.toList()),
+                    make.Return(make.Literal(false)));
+
+            return make.MethodDef(oldEqualsSym, make.Block(0, List.of(ifStat)));
+        } else {
+            return make.Block(SYNTHETIC, List.nil());
+        }
+    }
+
     public void visitMethodDef(JCMethodDecl tree) {
         if (tree.name == names.init && (currentClass.flags_field&ENUM) != 0) {
             // Add "String $enum$name, int $enum$ordinal" to the beginning of the
@@ -3411,6 +3739,30 @@
         }
     }
 
+    @Override
+    public void visitExec(JCExpressionStatement tree) {
+        if (tree.expr.hasTag(APPLY) &&
+                TreeInfo.name(((JCMethodInvocation)tree.expr).meth) == names._default) {
+            //inline constructor assignments
+            List<VarSymbol> vars = types.datumVars(currentClass.type).stream()
+                            .filter(v -> v.owner == currentClass)
+                            .collect(List.collector());
+            ListBuffer<JCStatement> stats = new ListBuffer<>();
+            List<JCExpression> args = ((JCMethodInvocation)tree.expr).args;
+            for (VarSymbol vsym : vars) {
+                stats.add(make.Exec(
+                        make.Assign(make.Select(makeThis(tree, currentClass), vsym), args.head)
+                                .setType(types.erasure(vsym.type))));
+                args = args.tail;
+            }
+            JCTree block = make.Block(0, stats.toList());
+            result = translate(block);
+            return;
+        } else {
+            super.visitExec(tree);
+        }
+    }
+
     public JCTree visitEnumSwitch(JCSwitch tree) {
         TypeSymbol enumSym = tree.selector.type.tsym;
         EnumMapping map = mapForEnum(tree.pos(), enumSym);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Tue Nov 07 11:10:12 2017 -0500
@@ -292,7 +292,8 @@
                 v.setLazyConstValue(initEnv(tree, initEnv), attr, tree);
             }
         }
-        if (chk.checkUnique(tree.pos(), v, enclScope)) {
+        if ((v.flags_field & (HYPOTHETICAL | DATUM)) != (HYPOTHETICAL | DATUM) &&
+                chk.checkUnique(tree.pos(), v, enclScope)) {
             chk.checkTransparentVar(tree.pos(), v, enclScope);
             enclScope.enter(v);
         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Nov 07 11:10:12 2017 -0500
@@ -1910,6 +1910,13 @@
                    List<Type> argtypes, List<Type> typeargtypes,
                    boolean allowBoxing, boolean useVarargs) {
         Symbol bestSoFar = methodNotFound;
+
+        if (name == names._default &&
+                env.enclMethod != null &&
+                env.enclMethod.sym.name == names.init) {
+            return env.info.datumImplicitConstructor;
+        }
+
         Env<AttrContext> env1 = env;
         boolean staticOnly = false;
         while (env1.outer != null) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Tue Nov 07 11:10:12 2017 -0500
@@ -28,6 +28,8 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import javax.tools.JavaFileObject;
 
@@ -53,6 +55,7 @@
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.ERROR;
+import static com.sun.tools.javac.code.TypeTag.NONE;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 
@@ -682,6 +685,8 @@
                                   true, false, false)
                 : (sym.fullname == names.java_lang_Object)
                 ? Type.noType
+                : ((tree.mods.flags & Flags.DATUM) != 0)
+                ? syms.dataClassType
                 : syms.objectType;
             }
             ct.supertype_field = modelMissingTypes(baseEnv, supertype, extending, false);
@@ -798,7 +803,7 @@
     private final class HeaderPhase extends AbstractHeaderPhase {
 
         public HeaderPhase() {
-            super(CompletionCause.HEADER_PHASE, new MembersPhase());
+            super(CompletionCause.HEADER_PHASE, new DatumPhase());
         }
 
         @Override
@@ -848,12 +853,10 @@
         }
     }
 
-    /** Enter member fields and methods of a class
-     */
-    private final class MembersPhase extends Phase {
+    private abstract class AbstractMembersPhase extends Phase {
 
-        public MembersPhase() {
-            super(CompletionCause.MEMBERS_PHASE, null);
+        public AbstractMembersPhase(CompletionCause completionCause, Phase next) {
+            super(completionCause, next);
         }
 
         private boolean completing;
@@ -877,6 +880,32 @@
                 completing = prevCompleting;
             }
         }
+    }
+
+    private final class DatumPhase extends AbstractMembersPhase {
+
+        public DatumPhase() {
+            super(CompletionCause.DATUM_PHASE, new MembersPhase());
+        }
+
+        @Override
+        protected void runPhase(Env<AttrContext> env) {
+            JCClassDecl tree = env.enclClass;
+            ClassSymbol sym = tree.sym;
+            if ((sym.flags_field & DATUM) != 0) {
+                memberEnter.memberEnter(TreeInfo.datumFields(tree), env);
+                memberEnter.memberEnter(TreeInfo.superDatumFields(tree), env);
+            }
+        }
+    }
+
+    /** Enter member fields and methods of a class
+     */
+    private final class MembersPhase extends AbstractMembersPhase {
+
+        public MembersPhase() {
+            super(CompletionCause.MEMBERS_PHASE, null);
+        }
 
         @Override
         protected void runPhase(Env<AttrContext> env) {
@@ -887,36 +916,21 @@
             // Add default constructor if needed.
             if ((sym.flags() & INTERFACE) == 0 &&
                 !TreeInfo.hasConstructors(tree.defs)) {
-                List<Type> argtypes = List.nil();
-                List<Type> typarams = List.nil();
-                List<Type> thrown = List.nil();
-                long ctorFlags = 0;
-                boolean based = false;
-                boolean addConstructor = true;
-                JCNewClass nc = null;
+                DefaultConstructorHelper helper = new BasicConstructorHelper(sym);
                 if (sym.name.isEmpty()) {
-                    nc = (JCNewClass)env.next.tree;
+                    JCNewClass nc = (JCNewClass)env.next.tree;
                     if (nc.constructor != null) {
-                        addConstructor = nc.constructor.kind != ERR;
-                        Type superConstrType = types.memberType(sym.type,
-                                                                nc.constructor);
-                        argtypes = superConstrType.getParameterTypes();
-                        typarams = superConstrType.getTypeArguments();
-                        ctorFlags = nc.constructor.flags() & VARARGS;
-                        if (nc.encl != null) {
-                            argtypes = argtypes.prepend(nc.encl.type);
-                            based = true;
+                        if (nc.constructor.kind != ERR) {
+                            helper = new AnonClassConstructorHelper(sym, (MethodSymbol)nc.constructor, nc.encl);
+                        } else {
+                            helper = null;
                         }
-                        thrown = superConstrType.getThrownTypes();
                     }
+                } else if ((sym.flags() & DATUM) != 0) {
+                    helper = new DatumConstructorHelper(sym, TreeInfo.datumFields(tree).map(vd -> vd.sym), TreeInfo.superDatumFields(tree));
                 }
-                if (addConstructor) {
-                    MethodSymbol basedConstructor = nc != null ?
-                            (MethodSymbol)nc.constructor : null;
-                    JCTree constrDef = DefaultConstructor(make.at(tree.pos), sym,
-                                                        basedConstructor,
-                                                        typarams, argtypes, thrown,
-                                                        ctorFlags, based);
+                if (helper != null) {
+                    JCTree constrDef = DefaultConstructor(make.at(tree.pos), helper);
                     tree.defs = tree.defs.prepend(constrDef);
                 }
             }
@@ -952,7 +966,12 @@
                 (types.supertype(tree.sym.type).tsym.flags() & Flags.ENUM) == 0) {
                 addEnumMembers(tree, env);
             }
-            memberEnter.memberEnter(tree.defs, env);
+            List<JCTree> defsToEnter = (tree.sym.flags_field & DATUM) != 0 ?
+                    tree.defs.diff(List.convert(JCTree.class, TreeInfo.datumFields(tree))) : tree.defs;
+            memberEnter.memberEnter(defsToEnter, env);
+            if ((tree.mods.flags & (DATUM | ABSTRACT)) == DATUM) {
+                addDatumMembersIfNeeded(tree, env);
+            }
 
             if (tree.sym.isAnnotationType()) {
                 Assert.check(tree.sym.isCompleted());
@@ -960,6 +979,37 @@
             }
         }
 
+        /** Add the accessors for fields to the symbol table.
+         */
+        private void addAccessorsIfNeeded(JCClassDecl tree, Env<AttrContext> env) {
+            tree.defs.stream()
+                    .filter(t -> t.hasTag(VARDEF))
+                    .map(t -> (JCVariableDecl)t)
+                    .filter(vd -> vd.accessors != null && vd.accessors.nonEmpty())
+                    .forEach(vd -> addAccessors(vd, env));
+        }
+
+        private void addAccessors(JCVariableDecl tree, Env<AttrContext> env) {
+            for (Pair<Accessors.Kind, Name> accessor : tree.accessors) {
+                Type accessorType = accessor.fst.accessorType(syms, tree.sym.type);
+                Symbol implSym = lookupMethod(env.enclClass.sym, accessor.snd, accessorType.getParameterTypes());
+                if (implSym == null || (implSym.flags_field & MANDATED) != 0) {
+                    JCMethodDecl getter = make.at(tree.pos).MethodDef(make.Modifiers(Flags.PUBLIC | Flags.MANDATED),
+                              accessor.snd,
+                              make.Type(accessorType.getReturnType()),
+                              List.nil(),
+                              accessorType.getParameterTypes().stream()
+                                      .map(ptype -> make.Param(tree.name, tree.sym.type, env.enclClass.sym))
+                                      .collect(List.collector()),
+                              List.nil(), // thrown
+                              null,
+                              null);
+                    memberEnter.memberEnter(getter, env);
+                    tree.sym.accessors = tree.sym.accessors.prepend(new Pair<>(accessor.fst, getter.sym));
+                }
+            }
+        }
+
         /** Add the implicit members for an enum type
          *  to the symbol table.
          */
@@ -994,136 +1044,269 @@
             memberEnter.memberEnter(valueOf, env);
         }
 
+        /** Add the implicit members for a datum type
+         *  to the symbol table.
+         */
+        private void addDatumMembersIfNeeded(JCClassDecl tree, Env<AttrContext> env) {
+
+            if (lookupMethod(tree.sym, names.toString, List.nil()) == null) {
+                JCExpression toStringType = make.Type(new ArrayType(tree.sym.type, syms.arrayClass));
+
+                // public String toString() { return ???; }
+                JCMethodDecl toString = make.
+                    MethodDef(make.Modifiers(Flags.PUBLIC | Flags.DATUM),
+                              names.toString,
+                              make.Type(syms.stringType),
+                              List.nil(),
+                              List.nil(),
+                              List.nil(), // thrown
+                              null,
+                              null);
+                memberEnter.memberEnter(toString, env);
+            }
+
+            if (lookupMethod(tree.sym, names.hashCode, List.nil()) == null) {
+                // public int hashCode() { return ???; }
+                JCMethodDecl hashCode = make.
+                    MethodDef(make.Modifiers(Flags.PUBLIC | Flags.DATUM),
+                              names.hashCode,
+                              make.Type(syms.intType),
+                              List.nil(),
+                              List.nil(),
+                              List.nil(), // thrown
+                              null,
+                              null);
+                memberEnter.memberEnter(hashCode, env);
+            }
+
+            if (lookupMethod(tree.sym, names.equals, List.of(syms.objectType)) == null) {
+                // public boolean equals(Object o) { return ???; }
+                JCMethodDecl equals = make.
+                    MethodDef(make.Modifiers(Flags.PUBLIC | Flags.DATUM),
+                              names.equals,
+                              make.Type(syms.booleanType),
+                              List.nil(),
+                              List.of(make.VarDef(make.Modifiers(Flags.PARAMETER),
+                                                names.fromString("o"),
+                                                make.Type(syms.objectType), null)),
+                              List.nil(), // thrown
+                              null,
+                              null);
+                memberEnter.memberEnter(equals, env);
+            }
+
+            addAccessorsIfNeeded(tree, env);
+        }
+
+    }
+
+    private Symbol lookupMethod(TypeSymbol tsym, Name name, List<Type> argtypes) {
+        for (Symbol s : tsym.members().getSymbolsByName(name, s -> s.kind == MTH)) {
+            if (types.isSameTypes(s.type.getParameterTypes(), argtypes)) {
+                return s;
+            }
+        }
+        return null;
     }
 
 /* ***************************************************************************
  * tree building
  ****************************************************************************/
 
-    /** Generate default constructor for given class. For classes different
-     *  from java.lang.Object, this is:
-     *
-     *    c(argtype_0 x_0, ..., argtype_n x_n) throws thrown {
-     *      super(x_0, ..., x_n)
-     *    }
-     *
-     *  or, if based == true:
-     *
-     *    c(argtype_0 x_0, ..., argtype_n x_n) throws thrown {
-     *      x_0.super(x_1, ..., x_n)
-     *    }
-     *
-     *  @param make     The tree factory.
-     *  @param c        The class owning the default constructor.
-     *  @param argtypes The parameter types of the constructor.
-     *  @param thrown   The thrown exceptions of the constructor.
-     *  @param based    Is first parameter a this$n?
-     */
-    JCTree DefaultConstructor(TreeMaker make,
-                            ClassSymbol c,
-                            MethodSymbol baseInit,
-                            List<Type> typarams,
-                            List<Type> argtypes,
-                            List<Type> thrown,
-                            long flags,
-                            boolean based) {
-        JCTree result;
-        if ((c.flags() & ENUM) != 0 &&
-            (types.supertype(c.type).tsym == syms.enumSym)) {
-            // constructors of true enums are private
-            flags = (flags & ~AccessFlags) | PRIVATE | GENERATEDCONSTR;
-        } else
-            flags |= (c.flags() & AccessFlags) | GENERATEDCONSTR;
-        if (c.name.isEmpty()) {
-            flags |= ANONCONSTR;
-        }
-        if (based) {
-            flags |= ANONCONSTR_BASED;
-        }
-        Type mType = new MethodType(argtypes, null, thrown, c);
-        Type initType = typarams.nonEmpty() ?
-            new ForAll(typarams, mType) :
-            mType;
-        MethodSymbol init = new MethodSymbol(flags, names.init,
-                initType, c);
-        init.params = createDefaultConstructorParams(make, baseInit, init,
-                argtypes, based);
-        List<JCVariableDecl> params = make.Params(argtypes, init);
-        List<JCStatement> stats = List.nil();
-        if (c.type != syms.objectType) {
-            stats = stats.prepend(SuperCall(make, typarams, params, based));
-        }
-        result = make.MethodDef(init, make.Block(0, stats));
-        return result;
+    interface DefaultConstructorHelper {
+       Type constructorType();
+       MethodSymbol constructorSymbol();
+       Type enclosingType();
+       TypeSymbol owner();
+       List<Name> superArgs();
+       List<Name> inits();
     }
 
-    private List<VarSymbol> createDefaultConstructorParams(
-            TreeMaker make,
-            MethodSymbol baseInit,
-            MethodSymbol init,
-            List<Type> argtypes,
-            boolean based) {
-        List<VarSymbol> initParams = null;
-        List<Type> argTypesList = argtypes;
-        if (based) {
-            /*  In this case argtypes will have an extra type, compared to baseInit,
-             *  corresponding to the type of the enclosing instance i.e.:
-             *
-             *  Inner i = outer.new Inner(1){}
-             *
-             *  in the above example argtypes will be (Outer, int) and baseInit
-             *  will have parameter's types (int). So in this case we have to add
-             *  first the extra type in argtypes and then get the names of the
-             *  parameters from baseInit.
-             */
-            initParams = List.nil();
-            VarSymbol param = new VarSymbol(PARAMETER, make.paramName(0), argtypes.head, init);
-            initParams = initParams.append(param);
-            argTypesList = argTypesList.tail;
+    class BasicConstructorHelper implements DefaultConstructorHelper {
+
+        TypeSymbol owner;
+        Type constructorType;
+        MethodSymbol constructorSymbol;
+
+        BasicConstructorHelper(TypeSymbol owner) {
+            this.owner = owner;
         }
-        if (baseInit != null && baseInit.params != null &&
-            baseInit.params.nonEmpty() && argTypesList.nonEmpty()) {
-            initParams = (initParams == null) ? List.nil() : initParams;
-            List<VarSymbol> baseInitParams = baseInit.params;
-            while (baseInitParams.nonEmpty() && argTypesList.nonEmpty()) {
-                VarSymbol param = new VarSymbol(baseInitParams.head.flags() | PARAMETER,
-                        baseInitParams.head.name, argTypesList.head, init);
-                initParams = initParams.append(param);
-                baseInitParams = baseInitParams.tail;
-                argTypesList = argTypesList.tail;
+
+        @Override
+        public Type constructorType() {
+            if (constructorType == null) {
+                constructorType = new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass);
             }
+            return constructorType;
         }
-        return initParams;
+
+        @Override
+        public MethodSymbol constructorSymbol() {
+            if (constructorSymbol == null) {
+                long flags;
+                if ((owner().flags() & ENUM) != 0 &&
+                    (types.supertype(owner().type).tsym == syms.enumSym)) {
+                    // constructors of true enums are private
+                    flags = PRIVATE | GENERATEDCONSTR;
+                } else {
+                    flags = (owner().flags() & AccessFlags) | GENERATEDCONSTR;
+                }
+                constructorSymbol = new MethodSymbol(flags, names.init,
+                    constructorType(), owner());
+            }
+            return constructorSymbol;
+        }
+
+        @Override
+        public Type enclosingType() {
+            return Type.noType;
+        }
+
+        @Override
+        public TypeSymbol owner() {
+            return owner;
+        }
+
+        @Override
+        public List<Name> superArgs() {
+            return List.nil();
+        }
+
+        @Override
+        public List<Name> inits() {
+            return List.nil();
+        }
     }
 
-    /** Generate call to superclass constructor. This is:
-     *
-     *    super(id_0, ..., id_n)
-     *
-     * or, if based == true
-     *
-     *    id_0.super(id_1,...,id_n)
-     *
-     *  where id_0, ..., id_n are the names of the given parameters.
-     *
-     *  @param make    The tree factory
-     *  @param params  The parameters that need to be passed to super
-     *  @param typarams  The type parameters that need to be passed to super
-     *  @param based   Is first parameter a this$n?
-     */
-    JCExpressionStatement SuperCall(TreeMaker make,
-                   List<Type> typarams,
-                   List<JCVariableDecl> params,
-                   boolean based) {
-        JCExpression meth;
-        if (based) {
-            meth = make.Select(make.Ident(params.head), names._super);
-            params = params.tail;
-        } else {
-            meth = make.Ident(names._super);
+    class AnonClassConstructorHelper extends BasicConstructorHelper {
+
+        MethodSymbol constr;
+        Type encl;
+        boolean based = false;
+
+        AnonClassConstructorHelper(TypeSymbol owner, MethodSymbol constr, JCExpression encl) {
+            super(owner);
+            this.constr = constr;
+            this.encl = encl != null ? encl.type : Type.noType;
         }
-        List<JCExpression> typeargs = typarams.nonEmpty() ? make.Types(typarams) : null;
-        return make.Exec(make.Apply(typeargs, meth, make.Idents(params)));
+
+        @Override
+        public Type constructorType() {
+            if (constructorType == null) {
+                Type ctype = types.memberType(owner.type, constr);
+                if (!enclosingType().hasTag(NONE)) {
+                    ctype = types.createMethodTypeWithParameters(ctype, ctype.getParameterTypes().prepend(enclosingType()));
+                    based = true;
+                }
+                constructorType = ctype;
+            }
+            return constructorType;
+        }
+
+        @Override
+        public MethodSymbol constructorSymbol() {
+            MethodSymbol csym = super.constructorSymbol();
+            csym.flags_field |= ANONCONSTR | (constr.flags() & VARARGS);
+            csym.flags_field |= based ? ANONCONSTR_BASED : 0;
+            ListBuffer<VarSymbol> params = new ListBuffer<>();
+            List<Type> argtypes = constructorType().getParameterTypes();
+            if (!enclosingType().hasTag(NONE)) {
+                argtypes = argtypes.tail;
+                params = params.prepend(new VarSymbol(PARAMETER, make.paramName(0), enclosingType(), csym));
+            }
+            if (constr.params != null) {
+                for (VarSymbol p : constr.params) {
+                    params.add(new VarSymbol(PARAMETER | p.flags(), p.name, argtypes.head, csym));
+                    argtypes = argtypes.tail;
+                }
+            }
+            csym.params = params.toList();
+            return csym;
+        }
+
+        @Override
+        public Type enclosingType() {
+            return encl;
+        }
+
+        @Override
+        public List<Name> superArgs() {
+            List<JCVariableDecl> params = make.Params(constructorType().getParameterTypes(), constructorSymbol());
+            if (!enclosingType().hasTag(NONE)) {
+                params = params.tail;
+            }
+            return params.map(vd -> vd.name);
+        }
+    }
+
+    class DatumConstructorHelper extends BasicConstructorHelper {
+
+        List<VarSymbol> datumFields;
+        List<JCVariableDecl> superFields;
+
+        DatumConstructorHelper(TypeSymbol owner, List<VarSymbol> datumFields, List<JCVariableDecl> superFields) {
+            super(owner);
+            this.datumFields = datumFields;
+            this.superFields = superFields;
+        }
+
+        @Override
+        public Type constructorType() {
+            if (constructorType == null) {
+                List<Type> argtypes = superFields.map(v -> v.vartype.type)
+                        .appendList(datumFields.map(v -> v.type));
+                constructorType = new MethodType(argtypes, syms.voidType, List.nil(), syms.methodClass);
+            }
+            return constructorType;
+        }
+
+        @Override
+        public MethodSymbol constructorSymbol() {
+            MethodSymbol csym = super.constructorSymbol();
+            ListBuffer<VarSymbol> params = new ListBuffer<>();
+            for (JCVariableDecl p : superFields) {
+                params.add(new VarSymbol(MANDATED | PARAMETER, p.name, p.vartype.type, csym));
+            }
+            for (VarSymbol p : datumFields) {
+                params.add(new VarSymbol(MANDATED | PARAMETER, p.name, p.type, csym));
+            }
+            csym.params = params.toList();
+            csym.flags_field |= DATUM | PUBLIC;
+            return csym;
+        }
+
+        @Override
+        public List<Name> superArgs() {
+            return superFields.map(v -> v.name);
+        }
+
+        @Override
+        public List<Name> inits() {
+            return datumFields.map(v -> v.name);
+        }
+    }
+
+    JCTree DefaultConstructor(TreeMaker make, DefaultConstructorHelper helper) {
+        Type initType = helper.constructorType();
+        MethodSymbol initSym = helper.constructorSymbol();
+        ListBuffer<JCStatement> stats = new ListBuffer<>();
+        if (helper.owner().type != syms.objectType) {
+            JCExpression meth;
+            if (!helper.enclosingType().hasTag(NONE)) {
+                meth = make.Select(make.Ident(initSym.params.head), names._super);
+            } else {
+                meth = make.Ident(names._super);
+            }
+            List<JCExpression> typeargs = initType.getTypeArguments().nonEmpty() ?
+                    make.Types(initType.getTypeArguments()) : null;
+            JCStatement superCall = make.Exec(make.Apply(typeargs, meth, helper.superArgs().map(make::Ident)));
+            stats.add(superCall);
+        }
+        for (Name initName : helper.inits()) {
+            stats.add(make.Exec(make.Assign(make.Select(make.Ident(names._this), initName), make.Ident(initName))));
+        }
+        JCTree result = make.MethodDef(initSym, make.Block(0, stats.toList()));
+        return result;
     }
 
     /**
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Tue Nov 07 11:10:12 2017 -0500
@@ -1578,7 +1578,9 @@
             ListBuffer<CompoundAnnotationProxy> proxies = new ListBuffer<>();
             for (int i = 0; i<numAttributes; i++) {
                 CompoundAnnotationProxy proxy = readCompoundAnnotation();
-                if (proxy.type.tsym == syms.proprietaryType.tsym)
+                if (proxy.type.tsym == syms.dataAnnotationType.tsym) {
+                    sym.flags_field |= DATUM;
+                } else if (proxy.type.tsym == syms.proprietaryType.tsym)
                     sym.flags_field |= PROPRIETARY;
                 else if (proxy.type.tsym == syms.profileType.tsym) {
                     if (profile != Profile.DEFAULT) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Nov 07 11:10:12 2017 -0500
@@ -30,6 +30,7 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Attribute.Compound;
 import com.sun.tools.javac.code.Attribute.TypeCompound;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 import com.sun.tools.javac.comp.*;
@@ -2170,6 +2171,13 @@
             this.endPosTable = toplevel.endPositions;
             c.pool = pool;
             pool.reset();
+            if (c.isDatum()) {
+                Attribute.Compound attribute = c.attribute(syms.dataAnnotationType.tsym);
+                if (attribute == null) {
+                    attribute = new Attribute.Compound(syms.dataAnnotationType, List.nil());
+                    c.appendAttributes(List.of(attribute));
+                }
+            }
             /* method normalizeDefs() can add references to external classes into the constant pool
              */
             cdef.defs = normalizeDefs(cdef.defs, c);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Tue Nov 07 11:10:12 2017 -0500
@@ -43,6 +43,7 @@
 import com.sun.source.util.JavacTask;
 import com.sun.tools.javac.api.JavacTaskImpl;
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Accessors.Kind;
 import com.sun.tools.javac.code.Attribute.Compound;
 import com.sun.tools.javac.code.Directive.ExportsDirective;
 import com.sun.tools.javac.code.Directive.ExportsFlag;
@@ -776,4 +777,23 @@
             throw new IllegalArgumentException(o.toString());
         return clazz.cast(o);
     }
+
+    @Override
+    public ExecutableElement getterFor(VariableElement variableElement) {
+        return accessorFor(Kind.GET, variableElement);
+    }
+
+    @Override
+    public ExecutableElement setterFor(VariableElement variableElement) {
+        return accessorFor(Kind.SET, variableElement);
+    }
+
+    private ExecutableElement accessorFor(Accessors.Kind kind, VariableElement variableElement) {
+        for (Pair<Accessors.Kind, MethodSymbol> accessor : ((VarSymbol)variableElement).accessors) {
+            if (accessor.fst == kind) {
+                return accessor.snd;
+            }
+        }
+        return null;
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java	Tue Nov 07 11:10:12 2017 -0500
@@ -30,6 +30,7 @@
 import java.util.Map;
 
 import com.sun.source.doctree.AttributeTree.ValueKind;
+import com.sun.source.doctree.DocTree;
 import com.sun.tools.javac.parser.DocCommentParser.TagParser.Kind;
 import com.sun.tools.javac.parser.Tokens.Comment;
 import com.sun.tools.javac.parser.Tokens.TokenKind;
@@ -1134,6 +1135,12 @@
                 }
             },
 
+            // {@getter text}
+            new AccessorParser(DCTree.Kind.GETTER),
+
+            // {@getter text}
+            new AccessorParser(DCTree.Kind.SETTER),
+
             // @param parameter-name description
             new TagParser(Kind.BLOCK, DCTree.Kind.PARAM) {
                 public DCTree parse(int pos) throws ParseException {
@@ -1316,4 +1323,15 @@
             tagParsers.put(names.fromString(p.getTreeKind().tagName), p);
 
     }
+
+    class AccessorParser extends TagParser {
+        AccessorParser(DocTree.Kind kind) {
+            super(Kind.BLOCK, kind, true);
+        }
+
+        public DCTree parse(int pos) throws ParseException {
+            List<DCTree> desc = blockContent();
+            return m.at(pos).newAccessorTree(treeKind, desc);
+        }
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Nov 07 11:10:12 2017 -0500
@@ -43,6 +43,7 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.List;
 
+import static com.sun.tools.javac.code.Flags.ABSTRACT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.*;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CASE;
@@ -2424,8 +2425,17 @@
         //todo: skip to anchor on error(?)
         int pos = token.pos;
         switch (token.kind) {
-        case RBRACE: case CASE: case DEFAULT: case EOF:
+        case RBRACE: case CASE: case EOF:
             return List.nil();
+        case DEFAULT:
+            if (peekToken(COLON)) {
+                return List.nil();
+            } else {
+                accept(DEFAULT);
+                JCExpression t = toP(F.at(pos).Ident(names._default));
+                t = arguments(null, t);
+                return List.of(toP(F.at(pos).Exec(t)));
+            }
         case LBRACE: case IF: case FOR: case WHILE: case DO: case TRY:
         case SWITCH: case SYNCHRONIZED: case RETURN: case THROW: case BREAK:
         case CONTINUE: case SEMI: case ELSE: case FINALLY: case CATCH:
@@ -2438,7 +2448,7 @@
             if (token.kind == INTERFACE ||
                 token.kind == CLASS ||
                 token.kind == ENUM) {
-                return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+                return List.of(classOrDatumOrInterfaceOrEnumDeclaration(mods, dc));
             } else {
                 JCExpression t = parseType(true);
                 return localVariableDeclarations(mods, t);
@@ -2447,16 +2457,16 @@
         case ABSTRACT: case STRICTFP: {
             Comment dc = token.comment(CommentStyle.JAVADOC);
             JCModifiers mods = modifiersOpt();
-            return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+            return List.of(classOrDatumOrInterfaceOrEnumDeclaration(mods, dc));
         }
         case INTERFACE:
         case CLASS:
             Comment dc = token.comment(CommentStyle.JAVADOC);
-            return List.of(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+            return List.of(classOrDatumOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
         case ENUM:
             error(token.pos, "local.enum");
             dc = token.comment(CommentStyle.JAVADOC);
-            return List.of(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+            return List.of(classOrDatumOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
         default:
             Token prevToken = token;
             JCExpression t = term(EXPR | TYPE);
@@ -2861,6 +2871,7 @@
             case STATIC      : flag = Flags.STATIC; break;
             case TRANSIENT   : flag = Flags.TRANSIENT; break;
             case FINAL       : flag = Flags.FINAL; break;
+            case NON_FINAL   : flag = Flags.NON_FINAL; break;
             case ABSTRACT    : flag = Flags.ABSTRACT; break;
             case NATIVE      : flag = Flags.NATIVE; break;
             case VOLATILE    : flag = Flags.VOLATILE; break;
@@ -3408,7 +3419,7 @@
             nextToken();
             return toP(F.at(pos).Skip());
         } else {
-            return classOrInterfaceOrEnumDeclaration(modifiersOpt(mods), docComment);
+            return classOrDatumOrInterfaceOrEnumDeclaration(modifiersOpt(mods), docComment);
         }
     }
 
@@ -3417,9 +3428,11 @@
      *  @param mods     Any modifiers starting the class or interface declaration
      *  @param dc       The documentation comment for the class, or null.
      */
-    protected JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) {
+    protected JCStatement classOrDatumOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) {
         if (token.kind == CLASS) {
             return classDeclaration(mods, dc);
+        } if (token.kind == DATUM) {
+            return datumDeclaration(mods, dc);
         } else if (token.kind == INTERFACE) {
             return interfaceDeclaration(mods, dc);
         } else if (token.kind == ENUM) {
@@ -3472,6 +3485,71 @@
         return result;
     }
 
+    protected JCClassDecl datumDeclaration(JCModifiers mods, Comment dc) {
+        int pos = token.pos;
+        accept(DATUM);
+        mods.flags |= Flags.DATUM;
+        Name name = typeName();
+
+        List<JCTypeParameter> typarams = typeParametersOpt();
+
+        Map<Name, JCTree> optHeaderFields = headerFields(mods);
+
+        if (optHeaderFields.size() == 0) {
+            log.error(token.pos, Errors.DatumMustDeclareAtLeastOneField);
+        }
+
+        JCExpression extending = null;
+        Set<Name> superFieldNames = new LinkedHashSet<>();
+        if (token.kind == EXTENDS) {
+            nextToken();
+            extending = parseType();
+            if (token.kind == LPAREN) {
+                accept(LPAREN);
+                while (token.kind != RPAREN) {
+                    Name id = ident();
+                    if (!superFieldNames.contains(id)) {
+                        superFieldNames.add(id);
+                    } else {
+                        log.error(token.pos, Errors.DuplicateArgumentToSuper(id));
+                    }
+                    if (token.kind == COMMA) {
+                        nextToken();
+                    }
+                }
+                accept(RPAREN);
+            }
+        }
+        List<JCExpression> implementing = List.nil();
+        if (token.kind == IMPLEMENTS) {
+            nextToken();
+            implementing = typeList();
+        }
+        List<JCTree> defs = List.nil();
+        if (token.kind == LBRACE) {
+            defs = classOrInterfaceBody(name, false);
+        } else {
+            accept(SEMI);
+        }
+        optHeaderFields.values().stream()
+                .filter(t -> superFieldNames.contains(((JCVariableDecl)t).name))
+                .forEach(t -> ((JCVariableDecl)t).getModifiers().flags |= Flags.HYPOTHETICAL);
+        ListBuffer<JCTree> fields = new ListBuffer<>();
+        for (Name n : superFieldNames) {
+            JCTree field = optHeaderFields.get(n);
+            fields.add(field);
+            optHeaderFields.remove(n);
+        }
+        for (JCTree field : optHeaderFields.values()) {
+            fields.add(field);
+        }
+        JCClassDecl result = toP(F.at(pos).ClassDef(
+            mods, name, typarams, extending, implementing,
+                defs.prependList(fields.toList())));
+        attach(result, dc);
+        return result;
+    }
+
     Name typeName() {
         int pos = token.pos;
         Name name = ident();
@@ -3481,6 +3559,33 @@
         return name;
     }
 
+    Map<Name, JCTree> headerFields(JCModifiers datumClassMods) {
+        accept(LPAREN);
+        Map<Name, JCTree> fields = new LinkedHashMap<>();
+        while (token.kind != RPAREN) {
+            JCModifiers mods = modifiersOpt();
+            mods.flags |= Flags.DATUM;
+            mods.flags |= (datumClassMods.flags & Flags.ABSTRACT) != 0 ? Flags.PROTECTED : 0;
+            if ((mods.flags & Flags.NON_FINAL) == 0) {
+                mods.flags |= Flags.FINAL;
+            }
+            JCExpression type = parseType();
+            int pos = token.pos;
+            Name id = ident();
+            if (!fields.containsKey(id)) {
+                List<Pair<Accessors.Kind, Name>> accessors = List.of(new Pair<>(Accessors.Kind.GET, id));
+                fields.put(id, toP(F.at(pos).VarDef(mods, id, type, null, accessors)));
+            } else {
+                log.error(pos, Errors.DatumCantDeclareDuplicateFields);
+            }
+            if (token.kind == COMMA) {
+                nextToken();
+            }
+        }
+        accept(RPAREN);
+        return fields;
+    }
+
     /** InterfaceDeclaration = INTERFACE Ident TypeParametersOpt
      *                         [EXTENDS TypeList] InterfaceBody
      *  @param mods    The modifiers starting the interface declaration
@@ -3675,9 +3780,10 @@
             int pos = token.pos;
             JCModifiers mods = modifiersOpt();
             if (token.kind == CLASS ||
+                token.kind == DATUM ||
                 token.kind == INTERFACE ||
                 token.kind == ENUM) {
-                return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+                return List.of(classOrDatumOrInterfaceOrEnumDeclaration(mods, dc));
             } else if (token.kind == LBRACE &&
                        (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0 &&
                        mods.annotations.isEmpty()) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Tokens.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Tokens.java	Tue Nov 07 11:10:12 2017 -0500
@@ -134,6 +134,7 @@
         ENUM("enum", Tag.NAMED),
         EXTENDS("extends"),
         FINAL("final"),
+        NON_FINAL("__nonfinal"),
         FINALLY("finally"),
         FLOAT("float", Tag.NAMED),
         FOR("for"),
@@ -151,6 +152,7 @@
         PRIVATE("private"),
         PROTECTED("protected"),
         PUBLIC("public"),
+        DATUM("__datum"),
         RETURN("return"),
         SHORT("short", Tag.NAMED),
         STATIC("static"),
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Nov 07 11:10:12 2017 -0500
@@ -317,6 +317,33 @@
 compiler.err.annotation.decl.not.allowed.here=\
     annotation type declaration not allowed here
 
+# 0: name
+compiler.err.duplicate.argument.to.super=\
+    duplicate argument {0}, arguments passed to super of a datum class must be unique
+
+compiler.err.datum.must.declare.at.least.one.field=\
+    datum must declare at least one field
+
+compiler.err.datum.cant.declare.duplicate.fields=\
+    datum cannot declare fields with the same name
+
+# 0: fragment
+compiler.err.cant.extend.datum=\
+    Illegal ''extends'' clause for datum class\n\
+    {0}
+
+compiler.misc.bad.datum.super=\
+    A datum class must extend DataClass or an ''abstract'' datum class.
+
+# 0: type, 1: name, 2: type, 3: name
+compiler.misc.super.field.mismatch=\
+    Superclass field declaration mismatch\n\
+    expected: {0} {1}\n\
+    found: {2} {3}
+
+compiler.misc.bad.super.fields=\
+    A datum class cannot have both an explicit constructor, and an implicit superclass header.
+
 # 0: symbol
 compiler.err.cant.inherit.from.final=\
     cannot inherit from final {0}
@@ -749,6 +776,11 @@
 compiler.misc.unexpected.ret.val=\
     unexpected return value
 
+# 0: name (accessor kind), 1: list of string (supported accessor kinds)
+compiler.err.unexpected.accessor.kind=\
+    unexpected accessor kind ''{0}''\n\
+    (Valid accessor kinds are: {1})
+
 # 0: set of flag
 compiler.err.mod.not.allowed.here=\
     modifier {0} not allowed here
@@ -2727,6 +2759,11 @@
     (use -source 8 or higher to enable lambda expressions)
 
 # 0: string
+compiler.err.accessors.not.supported.in.source=\
+    accessors syntax not supported in -source {0}\n\
+    (use -source 10 or higher to enable accessors syntax)
+
+# 0: string
 compiler.err.method.references.not.supported.in.source=\
     method references are not supported in -source {0}\n\
     (use -source 8 or higher to enable method references)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java	Tue Nov 07 11:10:12 2017 -0500
@@ -537,6 +537,32 @@
         }
     }
 
+    public static class DCAccessor extends DCInlineTag implements AccessorTree {
+        public final Kind kind;
+        public final List<? extends DocTree> description;
+
+        DCAccessor(Kind kind, List<? extends DocTree> description) {
+            Assert.check(kind == Kind.GETTER || kind == Kind.SETTER);
+            this.kind = kind;
+            this.description = description;
+        }
+
+        @Override @DefinedBy(Api.COMPILER_TREE)
+        public Kind getKind() {
+            return kind;
+        }
+
+        @Override @DefinedBy(Api.COMPILER_TREE)
+        public <R, D> R accept(DocTreeVisitor<R, D> v, D d) {
+            return v.visitAccessor(this, d);
+        }
+
+        @Override @DefinedBy(Api.COMPILER_TREE)
+        public List<? extends DocTree> getDescription() {
+            return description;
+        }
+    }
+
     public static class DCParam extends DCBlockTag implements ParamTree {
         public final boolean isTypeParameter;
         public final DCIdentifier name;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java	Tue Nov 07 11:10:12 2017 -0500
@@ -129,6 +129,19 @@
         }
     }
 
+    @Override
+    public Void visitAccessor(AccessorTree node, Void aVoid) {
+        try {
+            print("{");
+            printTagName(node);
+            print(node.getDescription());
+            print("}");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return null;
+    }
+
     @Override @DefinedBy(Api.COMPILER_TREE)
     public Void visitAttribute(AttributeTree node, Void p) {
         try {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java	Tue Nov 07 11:10:12 2017 -0500
@@ -53,6 +53,7 @@
 import com.sun.tools.javac.parser.ReferenceParser;
 import com.sun.tools.javac.parser.Tokens.Comment;
 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
+import com.sun.tools.javac.tree.DCTree.DCAccessor;
 import com.sun.tools.javac.tree.DCTree.DCAttribute;
 import com.sun.tools.javac.tree.DCTree.DCAuthor;
 import com.sun.tools.javac.tree.DCTree.DCComment;
@@ -332,6 +333,13 @@
     }
 
     @Override @DefinedBy(Api.COMPILER_TREE)
+    public DCAccessor newAccessorTree(Kind kind, List<? extends DocTree> desc) {
+        DCAccessor tree = new DCAccessor(kind, desc);
+        tree.pos = pos;
+        return tree;
+    }
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
     public DCParam newParamTree(boolean isTypeParameter, IdentifierTree name, List<? extends DocTree> description) {
         DCParam tree = new DCParam(isTypeParameter, (DCIdentifier) name, cast(description));
         tree.pos = pos;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Tue Nov 07 11:10:12 2017 -0500
@@ -922,23 +922,27 @@
         public VarSymbol sym;
         /** explicit start pos */
         public int startPos = Position.NOPOS;
+        /** accessors */
+        public List<Pair<Accessors.Kind, Name>> accessors;
 
         protected JCVariableDecl(JCModifiers mods,
                          Name name,
                          JCExpression vartype,
                          JCExpression init,
-                         VarSymbol sym) {
+                         VarSymbol sym,
+                         List<Pair<Accessors.Kind, Name>> accessors) {
             this.mods = mods;
             this.name = name;
             this.vartype = vartype;
             this.init = init;
             this.sym = sym;
+            this.accessors = accessors;
         }
 
         protected JCVariableDecl(JCModifiers mods,
                          JCExpression nameexpr,
                          JCExpression vartype) {
-            this(mods, null, vartype, null, null);
+            this(mods, null, vartype, null, null, null);
             this.nameexpr = nameexpr;
             if (nameexpr.hasTag(Tag.IDENT)) {
                 this.name = ((JCIdent)nameexpr).name;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Tue Nov 07 11:10:12 2017 -0500
@@ -160,6 +160,22 @@
         }
     }
 
+    public static List<JCVariableDecl> datumFields(JCClassDecl tree) {
+        return tree.defs.stream()
+                .filter(t -> t.hasTag(VARDEF))
+                .map(t -> (JCVariableDecl)t)
+                .filter(vd -> (vd.getModifiers().flags & (Flags.DATUM | HYPOTHETICAL)) == DATUM)
+                .collect(List.collector());
+    }
+
+    public static List<JCVariableDecl> superDatumFields(JCClassDecl tree) {
+        return tree.defs.stream()
+                .filter(t -> t.hasTag(VARDEF))
+                .map(t -> (JCVariableDecl)t)
+                .filter(vd -> (vd.getModifiers().flags & (Flags.DATUM | HYPOTHETICAL)) == (DATUM | HYPOTHETICAL))
+                .collect(List.collector());
+    }
+
     /** Is this a constructor whose first (non-synthetic) statement is not
      *  of the form this(...)?
      */
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Tue Nov 07 11:10:12 2017 -0500
@@ -209,7 +209,11 @@
     }
 
     public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init) {
-        JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
+        return VarDef(mods, name, vartype, init, null);
+    }
+
+    public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init, List<Pair<Accessors.Kind, Name>> accessors) {
+        JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null, accessors);
         tree.pos = pos;
         return tree;
     }
@@ -808,7 +812,7 @@
                 v.name,
                 Type(v.type),
                 init,
-                v).setPos(pos).setType(v.type);
+                v, null).setPos(pos).setType(v.type);
     }
 
     /** Create annotation trees from annotations.
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java	Tue Nov 07 11:10:12 2017 -0500
@@ -91,6 +91,7 @@
         HIERARCHY_PHASE,
         IMPORTS_PHASE,
         MEMBER_ENTER,
+        DATUM_PHASE,
         MEMBERS_PHASE,
         OTHER;
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Tue Nov 07 11:10:12 2017 -0500
@@ -74,6 +74,8 @@
     public final Name uses;
     public final Name open;
     public final Name with;
+    public final Name get;
+    public final Name set;
 
     // field and method names
     public final Name _name;
@@ -87,6 +89,7 @@
     public final Name deserializeLambda;
     public final Name desiredAssertionStatus;
     public final Name equals;
+    public final Name oldEquals;
     public final Name error;
     public final Name family;
     public final Name finalize;
@@ -203,6 +206,11 @@
     public final Name makeConcat;
     public final Name makeConcatWithConstants;
 
+    // members of java.lang.invoke.ObjectMethodBuilders
+    public final Name makeEquals;
+    public final Name makeHashCode;
+    public final Name makeToString;
+
     public final Name.Table table;
 
     public Names(Context context) {
@@ -236,6 +244,8 @@
         uses = fromString("uses");
         open = fromString("open");
         with = fromString("with");
+        get = fromString("get");
+        set = fromString("set");
 
         // field and method names
         _name = fromString("name");
@@ -249,6 +259,7 @@
         deserializeLambda = fromString("$deserializeLambda$");
         desiredAssertionStatus = fromString("desiredAssertionStatus");
         equals = fromString("equals");
+        oldEquals = fromString("oldEquals");
         error = fromString("<error>");
         family = fromString("family");
         finalize = fromString("finalize");
@@ -364,6 +375,10 @@
         // string concat
         makeConcat = fromString("makeConcat");
         makeConcatWithConstants = fromString("makeConcatWithConstants");
+
+        makeEquals = fromString("makeEquals");
+        makeHashCode = fromString("makeHashCode");
+        makeToString = fromString("makeToString");
     }
 
     protected Name.Table createTable(Options options) {
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Tue Nov 07 11:10:12 2017 -0500
@@ -28,6 +28,7 @@
 import java.util.List;
 
 import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.ModuleElement;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
@@ -36,6 +37,7 @@
 import javax.lang.model.util.SimpleElementVisitor9;
 
 import com.sun.source.doctree.DocTree;
+import com.sun.source.doctree.DocTree.Kind;
 import com.sun.source.doctree.IndexTree;
 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
@@ -312,6 +314,26 @@
 
     }
 
+    public Content accessorTagOutput(Element holder, List<? extends DocTree> tags) {
+        if (!tags.isEmpty()) {
+            //Todo: check that there's only one tag
+            DocTree.Kind kind = tags.get(0).getKind();
+            ExecutableElement accessor = utils.findAccessorFor((VariableElement)holder, kind);
+            //add reference to getter/setter
+            Content body = htmlWriter.getDocLink(LinkInfoImpl.Kind.SEE_TAG, (TypeElement)holder.getEnclosingElement(),
+                    accessor, accessor.getSimpleName() + utils.makeSignature(accessor, true), false, false);
+            ContentBuilder result = new ContentBuilder();
+            String key = kind == Kind.GETTER ?
+                    "doclet.getter" : "doclet.setter";
+            result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.seeLabel,
+                    new StringContent(configuration.getText(key)))));
+            result.addContent(HtmlTree.DD(body));
+            return result;
+        } else {
+            return new ContentBuilder();
+        }
+    }
+
     private void appendSeparatorIfNotEmpty(ContentBuilder body) {
         if (!body.isEmpty()) {
             body.addContent(", ");
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java	Tue Nov 07 11:10:12 2017 -0500
@@ -336,6 +336,7 @@
     public final Map<TypeElement, List<Element>> propertiesCache = new HashMap<>();
     public final Map<Element, Element> classPropertiesMap = new HashMap<>();
     public final Map<Element, GetterSetter> getterSetterMap = new HashMap<>();
+    public final Map<Element,Element> classAccessedFieldMap = new HashMap<>();
 
     public DocFileFactory docFileFactory;
 
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/CommentUtils.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/CommentUtils.java	Tue Nov 07 11:10:12 2017 -0500
@@ -219,6 +219,17 @@
         utils.removeCommentHelper(element);
     }
 
+    public void setAccessorCommentTree(Element element, List<DocTree> fullBody,
+                                  List<DocTree> blockTags, Utils utils) {
+        DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, blockTags);
+        TreePath pathToEncl = utils.docTrees.getPath(element.getEnclosingElement());
+        dcTreesMap.put(element, new DocCommentDuo(pathToEncl, docTree));
+        // There maybe an entry with the original comments usually null,
+        // therefore remove that entry if it exists, and allow a new one
+        // to be reestablished.
+        utils.removeCommentHelper(element);
+    }
+
     /**
      * A simplistic container to transport a TreePath, DocCommentTree pair.
      * Here is why we need this:
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/MemberSummaryBuilder.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/MemberSummaryBuilder.java	Tue Nov 07 11:10:12 2017 -0500
@@ -33,6 +33,7 @@
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 
+import com.sun.source.doctree.AccessorTree;
 import com.sun.source.doctree.DocTree;
 import com.sun.source.doctree.DocTree.Kind;
 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
@@ -340,6 +341,10 @@
                 if (property != null) {
                     processProperty(visibleMemberMap, member, property);
                 }
+                final Element accessedField = visibleMemberMap.getAccessorMemberDoc(member);
+                if (accessedField != null) {
+                    processAccessor(accessedField, member);
+                }
                 List<? extends DocTree> firstSentenceTags = utils.getFirstSentenceTrees(member);
                 if (utils.isExecutableElement(member) && firstSentenceTags.isEmpty()) {
                     //Inherit comments from overriden or implemented method if
@@ -445,6 +450,51 @@
     }
 
     /**
+     * Process the an accessor method, e.g. a field getter/setter, so that it contains the documentation
+     * from the underlying field. The method adds the leading sentence, which might be derived from
+     * the field accessor taglet (if present). Certain tags are also copied from the underlying field
+     * documentation.
+     *
+     * @param member the member which is to be augmented.
+     * @param field the original field documentation.
+     */
+    private void processAccessor(Element field, Element member) {
+        CommentUtils cmtutils = configuration.cmtUtils;
+        final boolean isGetter = ((ExecutableElement)member).getParameters().isEmpty();
+        List<? extends DocTree> accessorTags = utils.getBlockTags(field, isGetter ? Kind.GETTER : Kind.SETTER);
+        //Todo: check that there's only one tag
+        List<? extends DocTree> description = ((AccessorTree)accessorTags.get(0)).getDescription();
+
+        List<DocTree> fullBody = new ArrayList<>();
+        List<DocTree> blockTags = new ArrayList<>();
+        //add description
+        if (description.isEmpty()) {
+            if (isGetter) {
+                String text = MessageFormat.format(
+                        configuration.getText("doclet.FieldGetterWithName"),
+                        utils.propertyName((ExecutableElement) member));
+                fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
+            } else {
+                String text = MessageFormat.format(
+                        configuration.getText("doclet.FieldSetterWithName"),
+                        utils.propertyName((ExecutableElement)member));
+                fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
+            }
+        } else {
+            fullBody.addAll(description);
+        }
+
+        // copy certain tags
+        List<? extends DocTree> tags = utils.getBlockTags(field, Kind.SINCE);
+        blockTags.addAll(tags);
+        if (getVisibleMemberMap(VisibleMemberMap.Kind.FIELDS)
+                .getMembers((TypeElement)field.getEnclosingElement()).contains(field)) {
+            blockTags.add(cmtutils.makeSeeTree("#" + field.getSimpleName(), field));
+        }
+        cmtutils.setAccessorCommentTree(member, fullBody, blockTags, utils);
+    }
+
+    /**
      * Test whether the method is a getter.
      * @param element property method documentation. Needs to be either property
      * method, property getter, or property setter.
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties	Tue Nov 07 11:10:12 2017 -0500
@@ -63,6 +63,8 @@
 doclet.PropertySetter=Sets the value of the property
 doclet.PropertyGetterWithName=Gets the value of the property {0}.
 doclet.PropertySetterWithName=Sets the value of the property {0}.
+doclet.FieldGetterWithName=Gets the value of the field {0}.
+doclet.FieldSetterWithName=Sets the value of the field {0}.
 doclet.Default=Default:
 doclet.Parameters=Parameters:
 doclet.TypeParameters=Type Parameters:
@@ -74,6 +76,8 @@
 doclet.Return_tag_on_void_method=@return tag cannot be used in method with void return type.
 doclet.See_Also=See Also:
 doclet.See=See:
+doclet.getter=Getter:
+doclet.setter=Setter:
 doclet.SerialData=Serial Data:
 doclet.Since=Since:
 doclet.Throws=Throws:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/AccessorTaglet.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017, 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 jdk.javadoc.internal.doclets.toolkit.taglets;
+
+import com.sun.source.doctree.DocTree;
+import jdk.javadoc.internal.doclets.toolkit.Content;
+import jdk.javadoc.internal.doclets.toolkit.util.Utils;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
+import java.util.List;
+
+/**
+ * A taglet that represents the @param tag.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
+ * @author Jamie Ho
+ */
+public class AccessorTaglet extends BaseTaglet {
+
+    DocTree.Kind kind;
+
+    /**
+     * Construct a ParamTaglet.
+     */
+    public AccessorTaglet(DocTree.Kind kind) {
+        name = kind.tagName;
+        this.kind = kind;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean inField() {
+        return true;
+    }
+
+        /**
+     * {@inheritDoc}
+     */
+    public boolean inMethod() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean inOverview() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean inModule() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean inPackage() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean inType() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isInlineTag() {
+        return false;
+    }
+
+    /**
+     * Given an array of <code>ParamTag</code>s,return its string representation.
+     * @param holder the member that holds the param tags.
+     * @param writer the TagletWriter that will write this tag.
+     * @return the TagletOutput representation of these <code>ParamTag</code>s.
+     */
+    public Content getTagletOutput(Element holder, TagletWriter writer) {
+        Utils utils = writer.configuration().utils;
+        return writer.accessorTagOutput(holder, utils.getBlockTags(holder, kind));
+    }
+}
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java	Tue Nov 07 11:10:12 2017 -0500
@@ -727,6 +727,8 @@
         addStandardTaglet(new CodeTaglet());
         addStandardTaglet(new IndexTaglet());
         addStandardTaglet(new SummaryTaglet());
+        addStandardTaglet(new AccessorTaglet(GETTER));
+        addStandardTaglet(new AccessorTaglet(SETTER));
 
         // Keep track of the names of standard tags for error
         // checking purposes. The following are not handled above.
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java	Tue Nov 07 11:10:12 2017 -0500
@@ -25,6 +25,7 @@
 
 package jdk.javadoc.internal.doclets.toolkit.taglets;
 
+import java.util.EnumSet;
 import java.util.List;
 
 import javax.lang.model.element.Element;
@@ -152,6 +153,15 @@
     protected abstract Content seeTagOutput(Element holder, List<? extends DocTree> seeTags);
 
     /**
+     * Return the accessor tag output.
+     *
+     * @param holder
+     * @param tags the accessor tags
+     * @return the output of the accessor tag.
+     */
+    protected abstract Content accessorTagOutput(Element holder, List<? extends DocTree> tags);
+
+    /**
      * Return the output for a simple tag.
      *
      * @param element
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java	Tue Nov 07 11:10:12 2017 -0500
@@ -207,6 +207,17 @@
         return null;
     }
 
+    public ExecutableElement findAccessorFor(VariableElement field, DocTree.Kind kind) {
+        switch (kind) {
+            case GETTER:
+                return elementUtils.getterFor(field);
+            case SETTER:
+                return elementUtils.setterFor(field);
+            default:
+                throw new IllegalStateException("Cannot get here!");
+        }
+    }
+
     /**
      * Test whether a class is a subclass of another class.
      *
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberMap.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberMap.java	Tue Nov 07 11:10:12 2017 -0500
@@ -136,6 +136,7 @@
     private final Map<TypeElement, List<Element>> propertiesCache;
     private final Map<Element, Element> classPropertiesMap;
     private final Map<Element, GetterSetter> getterSetterMap;
+    private final Map<Element, Element> classAccessedFieldMap;
 
     /**
      * Construct a VisibleMemberMap of the given type for the given class.
@@ -158,6 +159,7 @@
         propertiesCache = configuration.propertiesCache;
         classPropertiesMap = configuration.classPropertiesMap;
         getterSetterMap = configuration.getterSetterMap;
+        classAccessedFieldMap = configuration.classAccessedFieldMap;
         comparator  = utils.makeGeneralPurposeComparator();
         visibleClasses = new LinkedHashSet<>();
         new ClassMembers(typeElement, STARTLEVEL).build();
@@ -213,6 +215,10 @@
         return classPropertiesMap.get(element);
     }
 
+    public Element getAccessorMemberDoc(Element element) {
+        return classAccessedFieldMap.get(element);
+    }
+
     /**
      * Returns the getter documentation belonging to the given property method.
      * @param propertyMethod the method for which the getter is needed.
@@ -534,6 +540,7 @@
                                 ? utils.getAnnotationFieldsUnfiltered(te)
                                 : utils.getFieldsUnfiltered(te);
                     }
+                    processAccessors(list);
                     break;
                 case CONSTRUCTORS:
                     list = utils.getConstructors(te);
@@ -599,6 +606,19 @@
             return false;
         }
 
+        private void processAccessors(List<? extends Element> fields) {
+            for (Element e : fields) {
+                Element getter = utils.elementUtils.getterFor((VariableElement)e);
+                if (getter != null) {
+                    classAccessedFieldMap.put(getter, e);
+                }
+                Element setter = utils.elementUtils.setterFor((VariableElement)e);
+                if (setter != null) {
+                    classAccessedFieldMap.put(setter, e);
+                }
+            }
+        }
+
         private List<Element> properties(final TypeElement typeElement, final boolean filter) {
             final List<ExecutableElement> allMethods = filter
                     ? utils.getMethods(typeElement)
--- a/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Tue Nov 07 11:10:12 2017 -0500
@@ -193,6 +193,7 @@
         IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM),  //
         UNDERSCORE(TokenKind.UNDERSCORE, XERRO),  //  _
         CLASS(TokenKind.CLASS, XEXPR|XDECL1|XBRACESNEEDED),  //  class decl (MAPPED: DOTCLASS)
+        DATUM(TokenKind.DATUM, XEXPR|XDECL1|XSTART|XTERM),  //  class decl (MAPPED: DOTCLASS)
         MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1),  //  @
         IMPORT(TokenKind.IMPORT, XDECL1|XSTART),  //  import -- consider declaration
         SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART),  //  ;
@@ -223,6 +224,7 @@
         // Modifiers keywords
         ABSTRACT(TokenKind.ABSTRACT, XDECL1),  //  abstract
         FINAL(TokenKind.FINAL, XDECL1),  //  final
+        NONFINAL(TokenKind.NON_FINAL, XDECL1),  //  final
         NATIVE(TokenKind.NATIVE, XDECL1),  //  native
         STATIC(TokenKind.STATIC, XDECL1),  //  static
         STRICTFP(TokenKind.STRICTFP, XDECL1),  //  strictfp
--- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Tue Nov 07 11:10:12 2017 -0500
@@ -31,6 +31,7 @@
 import com.sun.tools.javac.parser.Tokens.Comment;
 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
 import com.sun.tools.javac.parser.Tokens.Token;
+
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM;
@@ -41,6 +42,7 @@
 import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID;
+
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.JCAnnotation;
 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -50,12 +52,16 @@
 import com.sun.tools.javac.tree.JCTree.JCStatement;
 import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
 import com.sun.tools.javac.tree.JCTree.Tag;
+
 import static com.sun.tools.javac.tree.JCTree.Tag.IDENT;
+
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Position;
 
+import static com.sun.tools.javac.parser.Tokens.TokenKind.DATUM;
+
 /**
  * This is a subclass of JavacParser which overrides one method with a modified
  * verson of that method designed to allow parsing of one "snippet" of Java
@@ -176,9 +182,10 @@
             default:
                 JCModifiers mods = modifiersOpt(pmods);
                 if (token.kind == CLASS
+                        || token.kind == DATUM
                         || token.kind == INTERFACE
                         || token.kind == ENUM) {
-                    return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc));
+                    return List.<JCTree>of(classOrDatumOrInterfaceOrEnumDeclaration(mods, dc));
                 } else {
                     int pos = token.pos;
                     List<JCTypeParameter> typarams = typeParametersOpt();
--- a/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java	Tue Nov 07 10:57:54 2017 -0500
+++ b/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java	Tue Nov 07 11:10:12 2017 -0500
@@ -26,7 +26,7 @@
  * @bug 8161013
  * @summary Verify that anonymous class binaries have the correct flags set
  * @modules jdk.jdeps/com.sun.tools.classfile
- * @run main AnonymousClassFlags
+ * @compile AnonymousClassFlags.java
  */
 
 import java.util.*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/CheckDatumMembersAccess.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @summary check that members of abtract datum has protected access
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @compile CheckDatumMembersAccess.java
+ * @run main CheckDatumMembersAccess
+ */
+
+import com.sun.tools.classfile.ClassFile;
+
+import java.io.File;
+
+import com.sun.tools.classfile.AccessFlags;
+import com.sun.tools.classfile.Field;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.javac.util.Assert;
+
+public class CheckDatumMembersAccess {
+
+    abstract __datum abtractDatum(int AbstractFieldToSearchFor) {}
+
+    __datum Datum(int AbstractFieldToSearchFor, int newField, __nonfinal int nonFinalField) {}
+
+    public static void main(String args[]) throws Throwable {
+        new CheckDatumMembersAccess().run();
+    }
+
+    void run() throws Throwable {
+        checkAbstractDatum();
+        checkNonAbstractDatum();
+    }
+
+    void checkAbstractDatum() throws Throwable {
+        File testClasses = new File(System.getProperty("test.classes"));
+        File file = new File(testClasses,
+                CheckDatumMembersAccess.class.getName() + "$abtractDatum.class");
+        ClassFile classFile = ClassFile.read(file);
+        for (Field f : classFile.fields) {
+            if (f.getName(classFile.constant_pool).equals("AbstractFieldToSearchFor")) {
+                Assert.check((f.access_flags.flags & AccessFlags.ACC_PROTECTED) != 0, "fields of abstract datum should be protected");
+            }
+        }
+
+        for (Method m : classFile.methods) {
+            Assert.check((m.access_flags.flags & AccessFlags.ACC_PUBLIC) != 0, "methods of abstract datum should be public");
+        }
+    }
+
+    void checkNonAbstractDatum() throws Throwable {
+        File testClasses = new File(System.getProperty("test.classes"));
+        File file = new File(testClasses,
+                CheckDatumMembersAccess.class.getName() + "$Datum.class");
+        ClassFile classFile = ClassFile.read(file);
+        for (Field f : classFile.fields) {
+            if (f.getName(classFile.constant_pool).equals("AbstractFieldToSearchFor") ||
+                f.getName(classFile.constant_pool).equals("newField")) {
+                Assert.check((f.access_flags.flags & AccessFlags.ACC_FINAL) != 0, "fields of datum should be final");
+            }
+            if (f.getName(classFile.constant_pool).equals("nonFinalField")) {
+                Assert.check((f.access_flags.flags & AccessFlags.ACC_FINAL) == 0, "non-final fields of datum should be mutable");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DataClassAsSuper.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,16 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary check that a datum can inherit from DataClass or an abstract datum class
+ * @compile/fail/ref=DataClassAsSuper.out -XDrawDiagnostics DataClassAsSuper.java
+ */
+
+class DataClassAsSuper {
+
+    // should extend DataClass or an abstract datum
+    __datum D1(int x) extends Object { }
+
+    __datum D2(int y) {}
+
+    // D2 is datum but not abstract
+    __datum D3(int y, int x) extends D2(y) {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DataClassAsSuper.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,3 @@
+DataClassAsSuper.java:10:31: compiler.err.cant.extend.datum: (compiler.misc.bad.datum.super)
+DataClassAsSuper.java:15:38: compiler.err.cant.extend.datum: (compiler.misc.bad.datum.super)
+2 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DataClassTest.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.file.PathFileObject;
+import combo.ComboTask;
+
+/**
+ * DataClassTest
+ *
+ * @test
+ * @library /tools/javac/lib
+ * @modules jdk.compiler/com.sun.tools.javac.file
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build combo.ComboTestHelper
+
+ * @run main DataClassTest
+ */
+public class DataClassTest extends combo.ComboInstance<DataClassTest> {
+
+    enum FieldTypeKind implements combo.ComboParameter {
+        BYTE("byte", byte.class),
+        SHORT("short", short.class),
+        CHAR("char", char.class),
+        INT("int", int.class),
+        LONG("long", long.class),
+        FLOAT("float", float.class),
+        DOUBLE("double", double.class),
+        BOOLEAN("boolean", boolean.class),
+        OBJECT("Object", Object.class),
+        STRING("String", String.class);
+
+        String retTypeStr;
+        Class<?> clazz;
+
+        FieldTypeKind(String retTypeStr, Class<?> clazz) {
+            this.retTypeStr = retTypeStr;
+            this.clazz = clazz;
+        }
+
+        public String expand(String optParameter) {
+            return retTypeStr;
+        }
+    }
+
+    static final Map<FieldTypeKind, List<Object>> dataValues
+            = Map.ofEntries(
+            Map.entry(FieldTypeKind.BYTE, List.of(Byte.MIN_VALUE, (byte) -4, (byte) -1, (byte) 0, (byte) 1, (byte) 4, Byte.MAX_VALUE)),
+            Map.entry(FieldTypeKind.SHORT, List.of(Short.MIN_VALUE, (short) -4, (short) -1, (short) 0, (short) 1, (short) 4, Short.MAX_VALUE)),
+            Map.entry(FieldTypeKind.CHAR, List.of(Character.MIN_VALUE, 'a', 'A', 'z', (char) 0, Character.MAX_VALUE)),
+            Map.entry(FieldTypeKind.INT, List.of(Integer.MIN_VALUE, (int) -4, (int) -1, (int) 0, (int) 1, (int) 4, Integer.MAX_VALUE)),
+            Map.entry(FieldTypeKind.LONG, List.of(Long.MIN_VALUE, (long) -4, (long) -1, (long) 0, (long) 1, (long) 4, Long.MAX_VALUE)),
+            Map.entry(FieldTypeKind.FLOAT, List.of(Float.MIN_VALUE, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 0.0f, 1.0f, -1.0f, 2.0f, -2.0f, Float.MAX_VALUE)),
+            Map.entry(FieldTypeKind.DOUBLE, List.of(Double.MIN_VALUE, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0.0d, 1.0d, -1.0d, 2.0d, -2.0d, Double.MAX_VALUE)),
+            Map.entry(FieldTypeKind.BOOLEAN, List.of(true, false)),
+            Map.entry(FieldTypeKind.OBJECT, Arrays.asList(null, 3, "foo", new String[] {"a"})),
+            Map.entry(FieldTypeKind.STRING, Arrays.asList(null, "", "foo", "bar"))
+    );
+
+    static final String sourceTemplate =
+            "__datum Data(#{FT[0]} f0, #{FT[1]} f1) { }";
+
+    public static void main(String... args) throws Exception {
+        new combo.ComboTestHelper<DataClassTest>()
+                .withArrayDimension("FT", (x, t, index) -> {
+                    x.fieldType[index] = t;
+                }, 2, FieldTypeKind.values())
+                .run(DataClassTest::new);
+    }
+
+    FieldTypeKind[] fieldType = new FieldTypeKind[2];
+
+    @Override
+    public void doWork() throws Throwable {
+        newCompilationTask()
+                .withSourceFromTemplate(sourceTemplate)
+                .generate(this::check);
+    }
+
+    void check(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
+        List<Object> f0s = dataValues.get(fieldType[0]);
+        List<Object> f1s = dataValues.get(fieldType[1]);
+
+        if (result.hasErrors() || result.hasWarnings())
+            fail("Compilation errors not expected: " + result.compilationInfo());
+
+        Iterable<? extends PathFileObject> pfoIt = (Iterable<? extends PathFileObject>) result.get();
+        PathFileObject pfo = pfoIt.iterator().next();
+        Class<?> clazz;
+        Constructor<?> ctor;
+        Method getterF0, getterF1, hashCodeMethod, equalsMethod, toStringMethod;
+        Field fieldF0, fieldF1;
+
+        try {
+            URL[] urls = new URL[] {pfo.getPath().getParent().toUri().toURL()};
+            ClassLoader cl = new URLClassLoader(urls);
+            clazz = cl.loadClass("Data");
+
+            ctor = clazz.getConstructor(fieldType[0].clazz, fieldType[1].clazz);
+            getterF0 = clazz.getMethod("f0");
+            getterF1 = clazz.getMethod("f1");
+            fieldF0 = clazz.getDeclaredField("f0");
+            fieldF1 = clazz.getDeclaredField("f1");
+            equalsMethod = clazz.getMethod("equals", Object.class);
+            hashCodeMethod = clazz.getMethod("hashCode");
+            toStringMethod = clazz.getMethod("toString");
+
+            if (getterF0.getReturnType() != fieldType[0].clazz
+                || getterF1.getReturnType() != fieldType[1].clazz
+                || fieldF0.getType() != fieldType[0].clazz
+                || fieldF1.getType() != fieldType[1].clazz)
+                fail("Unexpected field or getter type: " + result.compilationInfo());
+
+            for (AccessibleObject o : List.of(ctor, getterF0, getterF1, equalsMethod, hashCodeMethod, toStringMethod)) {
+                // @@@ Why do we need this?
+                o.setAccessible(true);
+            }
+
+            for (Object f0 : f0s) {
+                for (Object f1 : f1s) {
+                    // Create object
+                    Object datum = ctor.newInstance(f0, f1);
+
+                    // Test getters
+                    Object actualF0 = getterF0.invoke(datum);
+                    Object actualF1 = getterF1.invoke(datum);
+                    if (!Objects.equals(f0, actualF0) || !Objects.equals(f1, actualF1))
+                        fail(String.format("Getters don't report back right values for %s %s/%s, %s %s/%s",
+                                           fieldType[0].clazz, f0, actualF0,
+                                           fieldType[1].clazz, f1, actualF1));
+
+                    int hashCode = (int) hashCodeMethod.invoke(datum);
+                    int expectedHash = Objects.hash(f0, f1);
+                    // @@@ fail
+                    if (hashCode != expectedHash) {
+                        System.err.println(String.format("Hashcode not as expected: expected=%d, actual=%d",
+                                           expectedHash, hashCode));
+                    }
+
+                    String toString = (String) toStringMethod.invoke(datum);
+                    String expectedToString = String.format("Data[f0=%s, f1=%s]", f0, f1);
+                    if (!toString.equals(expectedToString)) {
+                        fail(String.format("ToString not as expected: expected=%s, actual=%s",
+                                           expectedToString, toString));
+                    }
+
+                    // Test equals
+                    for (Object f2 : f0s) {
+                        for (Object f3 : f1s) {
+                            Object other = ctor.newInstance(f2, f3);
+                            boolean isEqual = (boolean) equalsMethod.invoke(datum, other);
+                            boolean isEqualReverse = (boolean) equalsMethod.invoke(other, datum);
+                            boolean shouldEqual = Objects.equals(f0, f2) && Objects.equals(f1, f3);
+                            // @@@ fail
+                            if (shouldEqual != isEqual)
+                                System.err.println(String.format("Equals not as expected: %s %s/%s, %s %s/%s",
+                                                   fieldType[0].clazz, f0, f2,
+                                                   fieldType[1].clazz, f1, f3));
+                            if (isEqualReverse != isEqual)
+                                fail(String.format("Equals not symmetric: %s %s/%s, %s %s/%s",
+                                                   fieldType[0].clazz, f0, f2,
+                                                   fieldType[1].clazz, f1, f3));
+
+                        }
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            throw new AssertionError(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DatumCanNotDeclaredFieldsWithSameName.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,9 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary smoke negative test for datum classes
+ * @compile/fail/ref=DatumCanNotDeclaredFieldsWithSameName.out -XDrawDiagnostics DatumCanNotDeclaredFieldsWithSameName.java
+ */
+
+public class DatumCanNotDeclaredFieldsWithSameName {
+    __datum D1(int x, int x) { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DatumCanNotDeclaredFieldsWithSameName.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,2 @@
+DatumCanNotDeclaredFieldsWithSameName.java:8:27: compiler.err.datum.cant.declare.duplicate.fields
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DatumShouldDeclareAtLeastOneFieldTest.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,11 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary smoke negative test for datum classes
+ * @compile/fail/ref=DatumShouldDeclareAtLeastOneFieldTest.out -XDrawDiagnostics DatumShouldDeclareAtLeastOneFieldTest.java
+ */
+
+public class DatumShouldDeclareAtLeastOneFieldTest {
+    static abstract __datum D1() { }
+
+    static __datum D2() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/DatumShouldDeclareAtLeastOneFieldTest.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,3 @@
+DatumShouldDeclareAtLeastOneFieldTest.java:8:34: compiler.err.datum.must.declare.at.least.one.field
+DatumShouldDeclareAtLeastOneFieldTest.java:10:25: compiler.err.datum.must.declare.at.least.one.field
+2 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/Neg01.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,43 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary smoke negative test for datum classes
+ * @compile/fail/ref=Neg01.out -XDrawDiagnostics Neg01.java
+ */
+
+public class Neg01 {
+    static abstract __datum Sup1(int x, int y) { }
+
+    __datum Bad1(int x, int y, int z) extends Sup1(x, y, z) { } //too many super args
+
+    __datum Bad2(int x, int z) extends Sup1(x) { } //too few super args
+
+    __datum Bad3(int x, int z) extends Sup1(x, z) { } //name mismatch
+
+    __datum Bad4(int x, double y) extends Sup1(x, y) { } //type mismatch
+
+    __datum Bad5(int x, int y) extends Sup1(x, y) {
+        Bad5(int x, int y) { super(x, y); } //error: explicit constructor and super header
+    }
+
+    static class Sup2 { }
+
+    __datum Bad6(int x, int y) extends Object(x) { } //bad super header
+
+    __datum Bad7(int x, int y) extends Sup2 { } //non-datum superclass
+
+    static __datum Sup3(int x, int y) { }
+
+    __datum Bad7(int x, int y) extends Sup2 { } //non-abstract datum superclass
+
+    __datum Test(int x) {
+        Test(int x, int y) {
+            default(); //too few
+            default(x); //ok
+            default(x, y); //too many
+        }
+
+        void test() {
+           default(); //error - not in a constructor
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/Neg01.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,14 @@
+Neg01.java:30:5: compiler.err.already.defined: kindname.class, Neg01.Bad7, kindname.class, Neg01
+Neg01.java:10:5: compiler.err.cant.apply.symbol: kindname.constructor, Sup1, int,int, int,int,int, kindname.class, Neg01.Sup1, (compiler.misc.arg.length.mismatch)
+Neg01.java:12:5: compiler.err.cant.apply.symbol: kindname.constructor, Sup1, int,int, int, kindname.class, Neg01.Sup1, (compiler.misc.arg.length.mismatch)
+Neg01.java:14:40: compiler.err.cant.extend.datum: (compiler.misc.super.field.mismatch: int, y, int, z)
+Neg01.java:16:43: compiler.err.cant.extend.datum: (compiler.misc.super.field.mismatch: int, y, double, y)
+Neg01.java:16:5: compiler.err.cant.apply.symbol: kindname.constructor, Sup1, int,int, int,double, kindname.class, Neg01.Sup1, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.possible.loss.of.precision: double, int))
+Neg01.java:18:40: compiler.err.cant.extend.datum: (compiler.misc.bad.super.fields)
+Neg01.java:24:40: compiler.err.cant.extend.datum: (compiler.misc.bad.datum.super)
+Neg01.java:24:5: compiler.err.cant.apply.symbol: kindname.constructor, Object, compiler.misc.no.args, int, kindname.class, java.lang.Object, (compiler.misc.arg.length.mismatch)
+Neg01.java:26:40: compiler.err.cant.extend.datum: (compiler.misc.bad.datum.super)
+Neg01.java:34:20: compiler.err.cant.apply.symbol: kindname.constructor, Test, int, compiler.misc.no.args, kindname.class, Neg01.Test, (compiler.misc.arg.length.mismatch)
+Neg01.java:36:20: compiler.err.cant.apply.symbol: kindname.constructor, Test, int, int,int, kindname.class, Neg01.Test, (compiler.misc.arg.length.mismatch)
+Neg01.java:40:12: compiler.err.cant.resolve.location.args: kindname.method, default, , , (compiler.misc.location: kindname.class, Neg01.Test, null)
+13 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/Pos01.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @summary smoke test for datum classes
+ * @run main Pos01
+ */
+public class Pos01 {
+
+    static int assertCount;
+
+    static void assertTrue(boolean cond) {
+        assertCount++;
+        if (!cond) {
+            throw new AssertionError();
+        }
+    }
+
+    static abstract __datum Sup(int x, int y) { }
+
+    static __datum Foo(int x, int y, public int z) extends Sup(x, y);
+
+    public static void main(String[] args) {
+        Foo foo = new Foo(1, 2, 3);
+        Foo foo2 = new Foo(1, 2, 3);
+        Foo foo3 = new Foo(1, 2, 4);
+        assertTrue(foo.toString().equals("Foo[x=1, y=2, z=3]"));
+//        assertTrue(foo.hashCode() == java.util.Objects.hash(1, 2, 3));
+        assertTrue(foo.equals(foo2));
+        assertTrue(!foo.equals(foo3));
+        assertTrue(foo.x() == 1);
+        assertTrue(foo.y() == 2);
+        assertTrue(foo.z() == 3);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/Pos02.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @summary smoke test for DA/DU with explicit default calls
+ * @compile Pos02.java
+ */
+
+class Pos02 {
+    static __datum Foo(final int x) {
+        Foo(int x) {
+            default(x);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/SubDatumCannotPassDuplicateArgsToSuperTest.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,10 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary smoke negative test for datum classes
+ * @compile/fail/ref=SubDatumCannotPassDuplicateArgsToSuperTest.out -XDrawDiagnostics SubDatumCannotPassDuplicateArgsToSuperTest.java
+ */
+
+public class SubDatumCannotPassDuplicateArgsToSuperTest {
+    abstract __datum D1(int x, int y) { }
+    __datum D2(int x, int y, int z) extends D1(x, x) { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/SubDatumCannotPassDuplicateArgsToSuperTest.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,2 @@
+SubDatumCannotPassDuplicateArgsToSuperTest.java:9:52: compiler.err.duplicate.argument.to.super: x
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/SubDatumFieldsMustBeAPrefixOfParentTest.java	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,10 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary smoke negative test for datum classes
+ * @compile/fail/ref=SubDatumFieldsMustBeAPrefixOfParentTest.out -XDrawDiagnostics SubDatumFieldsMustBeAPrefixOfParentTest.java
+ */
+
+public class SubDatumFieldsMustBeAPrefixOfParentTest {
+    abstract __datum D1(int x, int y) { }
+    __datum D2(int x, int y, int z) extends D1(y, x) { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/SubDatumFieldsMustBeAPrefixOfParentTest.out	Tue Nov 07 11:10:12 2017 -0500
@@ -0,0 +1,2 @@
+SubDatumFieldsMustBeAPrefixOfParentTest.java:9:45: compiler.err.cant.extend.datum: (compiler.misc.super.field.mismatch: int, x, int, y)
+1 error