changeset 48235:08f552df175c datum

introducing guards to records
author vromero
date Thu, 30 Nov 2017 17:16:20 -0500
parents f39471b530ca
children 85ea92261382
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java test/langtools/tools/javac/datum/GuardsInRecordsTest.java
diffstat 7 files changed, 170 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Nov 30 17:16:20 2017 -0500
@@ -3765,7 +3765,22 @@
                             .filter(v -> v.owner == currentClass)
                             .collect(List.collector());
             ListBuffer<JCStatement> stats = new ListBuffer<>();
+
             List<JCExpression> args = ((JCMethodInvocation)tree.expr).args;
+
+            JCRecordDecl recordDecl = (JCRecordDecl)classdefs.get(currentClass);
+            if (recordDecl.guard != null) {
+                MethodSymbol guardSym = lookupMethod(
+                        tree.pos(),
+                        names.guard,
+                        currentClass.type,
+                        vars.map(v -> v.type));
+                JCExpression meth = make.Ident(guardSym);
+                JCMethodInvocation app = make.Apply(null, meth, args);
+                app.type = guardSym.type.asMethodType().restype;
+                JCStatement guardCall = make.Exec(app);
+                stats.add(guardCall);
+            }
             for (VarSymbol vsym : vars) {
                 stats.add(make.Exec(
                         make.Assign(make.Select(makeThis(tree, currentClass), vsym), args.head)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Nov 30 17:16:20 2017 -0500
@@ -28,8 +28,6 @@
 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;
 
@@ -56,7 +54,9 @@
 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.*;
 
 import com.sun.tools.javac.util.Dependencies.CompletionCause;
@@ -893,8 +893,34 @@
             JCClassDecl tree = env.enclClass;
             ClassSymbol sym = tree.sym;
             if ((sym.flags_field & RECORD) != 0) {
-                memberEnter.memberEnter(TreeInfo.recordFields(tree), env);
-                memberEnter.memberEnter(TreeInfo.superRecordFields(tree), env);
+                List<JCVariableDecl> recordFields = TreeInfo.recordFields(tree);
+                List<JCVariableDecl> superFields = TreeInfo.superRecordFields(tree);
+                memberEnter.memberEnter(recordFields, env);
+                memberEnter.memberEnter(superFields, env);
+                JCRecordDecl recordDecl = (JCRecordDecl)tree;
+                if (recordDecl.guard != null) {
+                    List<VarSymbol> recordSyms = recordFields.map(vd -> vd.sym);
+                    List<Type> argtypes = superFields.map(v -> v.vartype.type).appendList(recordSyms.map(v -> v.type));
+                    MethodType guardMT = new MethodType(argtypes, syms.voidType, List.nil(), syms.methodClass);
+                    MethodSymbol guardMS = new MethodSymbol(PRIVATE | RECORD, names.guard, guardMT, sym);
+
+                    ListBuffer<VarSymbol> params = new ListBuffer<>();
+                    for (JCVariableDecl p : superFields) {
+                        params.add(new VarSymbol(MANDATED | PARAMETER, p.name, p.vartype.type, guardMS));
+                    }
+                    for (VarSymbol p : recordSyms) {
+                        params.add(new VarSymbol(MANDATED | PARAMETER, p.name, p.type, guardMS));
+                    }
+                    guardMS.params = params.toList();
+
+                    JCUnary neg = make.Unary(Tag.NOT, recordDecl.guard);
+                    JCNewClass newException = make.NewClass(null, null,
+                            make.QualIdent(syms.illegalArgumentExceptionType.tsym),
+                            List.of(make.Literal(TypeTag.CLASS, "fields values are not accepted by the given guard")), null);
+                    JCStatement ifStm = make.If(neg, make.Throw(newException), null);
+                    JCMethodDecl guardDecl = make.MethodDef(guardMS, make.Block(0, List.of(ifStm)));
+                    tree.defs = tree.defs.prepend(guardDecl);
+                }
             }
         }
     }
@@ -927,10 +953,10 @@
                         }
                     }
                 } else if ((sym.flags() & RECORD) != 0) {
-                    helper = new RecordConstructorHelper(sym, TreeInfo.recordFields(tree).map(vd -> vd.sym), TreeInfo.superRecordFields(tree));
+                    helper = new RecordConstructorHelper(sym, ((JCRecordDecl)tree).guard, TreeInfo.recordFields(tree).map(vd -> vd.sym), TreeInfo.superRecordFields(tree));
                 }
                 if (helper != null) {
-                    JCTree constrDef = DefaultConstructor(make.at(tree.pos), helper);
+                    JCTree constrDef = defaultConstructor(make.at(tree.pos), helper);
                     tree.defs = tree.defs.prepend(constrDef);
                 }
             }
@@ -1120,6 +1146,9 @@
        TypeSymbol owner();
        List<Name> superArgs();
        List<Name> inits();
+       default JCExpression guard() {
+           return null;
+       }
     }
 
     class BasicConstructorHelper implements DefaultConstructorHelper {
@@ -1243,11 +1272,13 @@
 
         List<VarSymbol> recordFields;
         List<JCVariableDecl> superFields;
+        JCExpression guard;
 
-        RecordConstructorHelper(TypeSymbol owner, List<VarSymbol> recordFields, List<JCVariableDecl> superFields) {
+        RecordConstructorHelper(TypeSymbol owner, JCExpression guard, List<VarSymbol> recordFields, List<JCVariableDecl> superFields) {
             super(owner);
             this.recordFields = recordFields;
             this.superFields = superFields;
+            this.guard = guard;
         }
 
         @Override
@@ -1284,9 +1315,14 @@
         public List<Name> inits() {
             return recordFields.map(v -> v.name);
         }
+
+        @Override
+        public JCExpression guard() {
+            return guard;
+        }
     }
 
-    JCTree DefaultConstructor(TreeMaker make, DefaultConstructorHelper helper) {
+    JCTree defaultConstructor(TreeMaker make, DefaultConstructorHelper helper) {
         Type initType = helper.constructorType();
         MethodSymbol initSym = helper.constructorSymbol();
         ListBuffer<JCStatement> stats = new ListBuffer<>();
@@ -1302,6 +1338,14 @@
             JCStatement superCall = make.Exec(make.Apply(typeargs, meth, helper.superArgs().map(make::Ident)));
             stats.add(superCall);
         }
+        JCExpression guard = helper.guard();
+        if (guard != null) {
+            JCExpression meth = make.Ident(names.guard);
+            List<JCExpression> args = helper.superArgs().map(make::Ident);
+            args = args.appendList(helper.inits().map(make::Ident));
+            JCStatement guardCall = make.Exec(make.Apply(null, meth, args));
+            stats.add(guardCall);
+        }
         for (Name initName : helper.inits()) {
             stats.add(make.Exec(make.Assign(make.Select(make.Ident(names._this), initName), make.Ident(initName))));
         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Nov 30 17:16:20 2017 -0500
@@ -3530,7 +3530,12 @@
             nextToken();
             implementing = typeList();
         }
+        JCExpression whereExpr = null;
         List<JCTree> defs = List.nil();
+        if (token.kind == IDENTIFIER && token.name() == names.where) {
+            nextToken();
+            whereExpr = parseExpression();
+        }
         if (token.kind == LBRACE) {
             defs = classInterfaceOrRecordBody(name, false, true);
         } else {
@@ -3548,9 +3553,8 @@
         for (JCTree field : optHeaderFields.values()) {
             fields.add(field);
         }
-        JCClassDecl result = toP(F.at(pos).ClassDef(
-            mods, name, typarams, extending, implementing,
-                defs.prependList(fields.toList())));
+        defs = defs.prependList(fields.toList());
+        JCRecordDecl result = toP(F.at(pos).RecordDef(mods, name, typarams, extending, implementing, defs, whereExpr));
         attach(result, dc);
         return result;
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Nov 30 17:16:20 2017 -0500
@@ -814,6 +814,21 @@
         }
     }
 
+    public static class JCRecordDecl extends JCClassDecl implements ClassTree {
+        public JCExpression guard;
+        public JCRecordDecl(JCModifiers mods,
+                           Name name,
+                           List<JCTypeParameter> typarams,
+                           JCExpression extending,
+                           List<JCExpression> implementing,
+                           List<JCTree> defs,
+                           ClassSymbol sym,
+                           JCExpression guard) {
+            super(mods, name, typarams, extending, implementing, defs, sym);
+            this.guard = guard;
+        }
+    }
+
     /**
      * A method definition.
      */
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Nov 30 17:16:20 2017 -0500
@@ -171,6 +171,26 @@
         return tree;
     }
 
+    public JCRecordDecl RecordDef(JCModifiers mods,
+                                Name name,
+                                List<JCTypeParameter> typarams,
+                                JCExpression extending,
+                                List<JCExpression> implementing,
+                                List<JCTree> defs,
+                                JCExpression guard)
+    {
+        JCRecordDecl tree = new JCRecordDecl(mods,
+                                     name,
+                                     typarams,
+                                     extending,
+                                     implementing,
+                                     defs,
+                                     null,
+                                     guard);
+        tree.pos = pos;
+        return tree;
+    }
+
     public JCMethodDecl MethodDef(JCModifiers mods,
                                Name name,
                                JCExpression restype,
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Wed Nov 29 15:06:36 2017 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Thu Nov 30 17:16:20 2017 -0500
@@ -213,6 +213,8 @@
 
     // record related
     public final Name record;
+    public final Name where;
+    public final Name guard;
 
     public final Name.Table table;
 
@@ -383,6 +385,8 @@
         makeHashCode = fromString("makeHashCode");
         makeToString = fromString("makeToString");
         record = fromString("record");
+        where = fromString("where");
+        guard = fromString("$guard");
     }
 
     protected Name.Table createTable(Options options) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/datum/GuardsInRecordsTest.java	Thu Nov 30 17:16:20 2017 -0500
@@ -0,0 +1,57 @@
+/*
+ * 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 test for guards in records
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @run main GuardsInRecordsTest
+ */
+
+public class GuardsInRecordsTest {
+    static record Range1(int lo, int hi) where lo <= hi;
+    static record Range2(int lo, int hi) where lo <= hi {}
+    static record Range3(int lo, int hi) where lo <= hi {
+        Range3(int lo, int hi) {
+            default(lo, hi);
+        }
+    }
+
+    public static void main(String... args) {
+        try {
+            Range1 r = new Range1(2, 1);
+            throw new AssertionError("an exception was expected for Range1");
+        } catch (IllegalArgumentException iae) {}
+
+        try {
+            Range2 r = new Range2(2, 1);
+            throw new AssertionError("an exception was expected for Range2");
+        } catch (IllegalArgumentException iae) {}
+
+        try {
+            Range3 r = new Range3(2, 1);
+            throw new AssertionError("an exception was expected for Range3");
+        } catch (IllegalArgumentException iae) {}
+    }
+}