< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

Print this page

        

@@ -92,10 +92,12 @@
     private final JavaFileManager fileManager;
     private final Source source;
     private final Target target;
     private final Profile profile;
     private final boolean warnOnAnyAccessToMembers;
+    private final boolean allowGenericsOverValues;
+    private final boolean allowValueBasedClasses;
 
     // The set of lint options currently in effect. It is initialized
     // from the context, and then is set/reset as needed by Attr as it
     // visits all the various parts of the trees during attribution.
     private Lint lint;

@@ -132,11 +134,12 @@
         fileManager = context.get(JavaFileManager.class);
 
         source = Source.instance(context);
         target = Target.instance(context);
         warnOnAnyAccessToMembers = options.isSet("warnOnAccessToMembers");
-
+        allowGenericsOverValues = options.isSet("allowGenericsOverValues");
+        allowValueBasedClasses = options.isSet("allowValueBasedClasses");
         Target target = Target.instance(context);
         syntheticNameChar = target.syntheticNameChar();
 
         profile = Profile.instance(context);
 

@@ -448,11 +451,11 @@
 
     public void removeCompiled(ClassSymbol csym) {
         compiled.remove(Pair.of(csym.packge().modle, csym.flatname));
     }
 
-/* *************************************************************************
+    /* *************************************************************************
  * Type Checking
  **************************************************************************/
 
     /**
      * A check context is an object that can be used to perform compatibility

@@ -554,16 +557,23 @@
     Type checkType(final DiagnosticPosition pos, final Type found, final Type req, final CheckContext checkContext) {
         final InferenceContext inferenceContext = checkContext.inferenceContext();
         if (inferenceContext.free(req) || inferenceContext.free(found)) {
             inferenceContext.addFreeTypeListener(List.of(req, found),
                     solvedContext -> checkType(pos, solvedContext.asInstType(found), solvedContext.asInstType(req), checkContext));
+        } else {
+            if (found.hasTag(CLASS)) {
+                checkParameterizationWithValues(pos, found);
+            }
         }
         if (req.hasTag(ERROR))
             return req;
         if (req.hasTag(NONE))
             return found;
         if (checkContext.compatible(found, req, checkContext.checkWarner(pos, found, req))) {
+            if (found.hasTag(BOT) && types.isValueBased(req)) {
+                log.warning(pos, Warnings.SuspiciousMixOfNullWithValueBasedClass(req));
+            }
             return found;
         } else {
             if (found.isNumeric() && req.isNumeric()) {
                 checkContext.report(pos, diags.fragment(Fragments.PossibleLossOfPrecision(found, req)));
                 return types.createErrorType(found);

@@ -582,10 +592,17 @@
     Type checkCastable(DiagnosticPosition pos, Type found, Type req) {
         return checkCastable(pos, found, req, basicHandler);
     }
     Type checkCastable(DiagnosticPosition pos, Type found, Type req, CheckContext checkContext) {
         if (types.isCastable(found, req, castWarner(pos, found, req))) {
+            if (types.isValueBased(req)) {
+                if (found.hasTag(BOT)) {
+                    log.warning(pos, Warnings.SuspiciousMixOfNullWithValueBasedClass(req));
+                } else if (!types.isValueBased(found)) {
+                    log.warning(pos, Warnings.PotentialNullPollution(found));
+                }
+            }
             return req;
         } else {
             checkContext.report(pos, diags.fragment(Fragments.InconvertibleTypes(found, req)));
             return types.createErrorType(found);
         }

@@ -731,29 +748,39 @@
 
     /** Check that type is a reference type, i.e. a class, interface or array type
      *  or a type variable.
      *  @param pos           Position to be used for error reporting.
      *  @param t             The type to be checked.
+     *  @param valueOK       If false, a value class does not qualify
      */
-    Type checkRefType(DiagnosticPosition pos, Type t) {
-        if (t.isReference())
+    Type checkRefType(DiagnosticPosition pos, Type t, boolean valueOK) {
+        if (t.isReference() && (valueOK || !types.isValue(t)))
             return t;
         else
             return typeTagError(pos,
                                 diags.fragment(Fragments.TypeReqRef),
                                 t);
     }
 
+    /** Check that type is a reference type, i.e. a class, interface or array type
+     *  or a type variable.
+     *  @param pos           Position to be used for error reporting.
+     *  @param t             The type to be checked.
+     */
+    Type checkRefType(DiagnosticPosition pos, Type t) {
+        return checkRefType(pos, t, true);
+    }
+
     /** Check that each type is a reference type, i.e. a class, interface or array type
      *  or a type variable.
      *  @param trees         Original trees, used for error reporting.
      *  @param types         The types to be checked.
      */
     List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
         List<JCExpression> tl = trees;
         for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
-            l.head = checkRefType(tl.head.pos(), l.head);
+            l.head = checkRefType(tl.head.pos(), l.head, allowGenericsOverValues);
             tl = tl.tail;
         }
         return types;
     }
 

@@ -785,10 +812,60 @@
             return false;
         } else
             return true;
     }
 
+    void checkParameterizationWithValues(DiagnosticPosition pos, Type t) {
+        if (!allowGenericsOverValues && t.tsym != syms.classType.tsym) { // tolerate Value.class for now.
+            valueParameterizationChecker.visit(t, pos);
+        }
+    }
+
+    /** valueParameterizationChecker: A type visitor that descends down the given type looking for instances of value types
+     *  being used as type arguments and issues error against those usages.
+     */
+    private final Types.SimpleVisitor<Void, DiagnosticPosition> valueParameterizationChecker = new Types.SimpleVisitor<Void, DiagnosticPosition>() {
+
+        @Override
+        public Void visitType(Type t, DiagnosticPosition pos) {
+            return null;
+        }
+
+        @Override
+        public Void visitClassType(ClassType t, DiagnosticPosition pos) {
+            for (Type targ : t.allparams()) {
+                if (types.isValue(targ) && !allowGenericsOverValues) {
+                    log.error(pos, Errors.GenericParameterizationWithValueType(t));
+                }
+                visit(targ, pos);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitTypeVar(TypeVar t, DiagnosticPosition pos) {
+             return null;
+        }
+
+        @Override
+        public Void visitCapturedType(CapturedType t, DiagnosticPosition pos) {
+            return null;
+        }
+
+        @Override
+        public Void visitArrayType(ArrayType t, DiagnosticPosition pos) {
+            return visit(t.elemtype, pos);
+        }
+
+        @Override
+        public Void visitWildcardType(WildcardType t, DiagnosticPosition pos) {
+            return visit(t.type, pos);
+        }
+    };
+
+
+
     /** Check that usage of diamond operator is correct (i.e. diamond should not
      * be used with non-generic classes or in anonymous class creation expressions)
      */
     Type checkDiamond(JCNewClass tree, Type t) {
         if (!TreeInfo.isDiamond(tree) ||

@@ -933,11 +1010,15 @@
             log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferVoid));
             return types.createErrorType(t);
         }
 
         //upward project the initializer type
-        return types.upward(t, types.captures(t));
+        Type varType = types.upward(t, types.captures(t));
+        if (varType.hasTag(CLASS)) {
+            checkParameterizationWithValues(pos, varType);
+        }
+        return varType;
     }
 
     Type checkMethod(final Type mtype,
             final Symbol sym,
             final Env<AttrContext> env,

@@ -1131,12 +1212,16 @@
                 mask = ReceiverParamFlags;
             else if (sym.owner.kind != TYP)
                 mask = LocalVarFlags;
             else if ((sym.owner.flags_field & INTERFACE) != 0)
                 mask = implicit = InterfaceVarFlags;
-            else
+            else {
                 mask = VarFlags;
+                if (types.isValue(sym.owner.type) && (flags & STATIC) == 0) {
+                    implicit |= FINAL;
+                }
+            }
             break;
         case MTH:
             if (sym.name == names.init) {
                 if ((sym.owner.flags_field & ENUM) != 0) {
                     // enum constructors cannot be declared public or

@@ -1158,11 +1243,13 @@
                     }
                 } else {
                     mask = implicit = InterfaceMethodFlags;
                 }
             } else {
-                mask = MethodFlags;
+                // instance methods of value types do not have a monitor associated with their `this'
+                mask = ((sym.owner.flags_field & VALUE) != 0 && (flags & Flags.STATIC) == 0) ?
+                        MethodFlags & ~SYNCHRONIZED : MethodFlags;
             }
             // Imply STRICTFP if owner has STRICTFP set.
             if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
                 ((flags) & Flags.DEFAULT) != 0)
                 implicit |= sym.owner.flags_field & STRICTFP;

@@ -1187,12 +1274,12 @@
             }
             // Interfaces are always ABSTRACT
             if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
 
             if ((flags & ENUM) != 0) {
-                // enums can't be declared abstract or final
-                mask &= ~(ABSTRACT | FINAL);
+                // enums can't be declared abstract or final or value type
+                mask &= ~(ABSTRACT | FINAL | VALUE);
                 implicit |= implicitEnumFinalFlag(tree);
             }
             // Imply STRICTFP if owner has STRICTFP set.
             implicit |= sym.owner.flags_field & STRICTFP;
             break;

@@ -1221,11 +1308,11 @@
                                 STATIC | PRIVATE,
                                 DEFAULT)
                  &&
                  checkDisjoint(pos, flags,
                                ABSTRACT | INTERFACE,
-                               FINAL | NATIVE | SYNCHRONIZED)
+                               FINAL | NATIVE | SYNCHRONIZED | VALUE)
                  &&
                  checkDisjoint(pos, flags,
                                PUBLIC,
                                PRIVATE | PROTECTED)
                  &&

@@ -2031,11 +2118,12 @@
         final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
         // Check if this method must override a super method due to being annotated with @Override
         // or by virtue of being a member of a diamond inferred anonymous class. Latter case is to
         // be treated "as if as they were annotated" with @Override.
         boolean mustOverride = explicitOverride ||
-                (env.info.isAnonymousDiamond && !m.isConstructor() && !m.isPrivate());
+                (env.info.isAnonymousDiamond && !m.isConstructor() && !m.isPrivate() &&
+                        (!m.owner.isValue() || (tree.body.flags & SYNTHETIC) == 0));
         if (mustOverride && !isOverrider(m)) {
             DiagnosticPosition pos = tree.pos();
             for (JCAnnotation a : tree.getModifiers().annotations) {
                 if (a.annotationType.type.tsym == syms.overrideType.tsym) {
                     pos = a.pos();

@@ -2155,10 +2243,49 @@
             log.error(pos,
                       Errors.DoesNotOverrideAbstract(c, undef1, undef1.location()));
         }
     }
 
+    // A value class cannot contain a field of its own type either or indirectly.
+    void checkNonCyclicMembership(JCClassDecl tree) {
+        Assert.check((tree.sym.flags_field & LOCKED) == 0);
+        try {
+            tree.sym.flags_field |= LOCKED;
+            for (List<? extends JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+                if (l.head.hasTag(VARDEF)) {
+                    JCVariableDecl field = (JCVariableDecl) l.head;
+                    if (cyclePossible(field.sym)) {
+                        Type fieldType = field.sym.type;
+                        checkNonCyclicMembership((ClassSymbol) fieldType.tsym, field.pos());
+                    }
+                }
+            }
+        } finally {
+            tree.sym.flags_field &= ~LOCKED;
+        }
+
+    }
+    // where
+    private void checkNonCyclicMembership(ClassSymbol c, DiagnosticPosition pos) {
+        if ((c.flags_field & LOCKED) != 0) {
+            log.error(pos, Errors.CyclicValueTypeMembership(c));
+            return;
+        }
+        try {
+            c.flags_field |= LOCKED;
+            for (Symbol fld : c.members().getSymbols(s -> s.kind == VAR && cyclePossible((VarSymbol) s), NON_RECURSIVE)) {
+                checkNonCyclicMembership((ClassSymbol) fld.type.tsym, pos);
+            }
+        } finally {
+            c.flags_field &= ~LOCKED;
+        }
+    }
+        // where
+        private boolean cyclePossible(VarSymbol symbol) {
+            return (symbol.flags() & STATIC) == 0 && types.isValue(symbol.type);
+        }
+
     void checkNonCyclicDecl(JCClassDecl tree) {
         CycleChecker cc = new CycleChecker();
         cc.scan(tree);
         if (!cc.errorFound && !cc.partialCheck) {
             tree.sym.flags_field |= ACYCLIC;

@@ -2838,10 +2965,17 @@
                 log.error(a.pos(), Errors.BadFunctionalIntfAnno);
             } else if (!s.isInterface() || (s.flags() & ANNOTATION) != 0) {
                 log.error(a.pos(), Errors.BadFunctionalIntfAnno1(Fragments.NotAFunctionalIntf(s)));
             }
         }
+        if (a.annotationType.type.tsym == syms.valueBasedType.tsym) {
+            if (s.isInterface() || s.isEnum()) {
+                log.error(a.pos(), Errors.BadValueBasedAnno);
+            } else if (allowValueBasedClasses) {
+                s.flags_field |= VALUEBASED;
+            }
+        }
     }
 
     public void validateTypeAnnotation(JCAnnotation a, boolean isTypeParameter) {
         Assert.checkNonNull(a.type);
         validateAnnotationTree(a);
< prev index next >