8038263: Refactor annotation handling after actualEnterAnnotations
authoremc
Thu, 03 Apr 2014 20:28:23 -0400
changeset 23814 06ab27895804
parent 23813 1a2765f25d5f
child 23815 4af253e666e5
8038263: Refactor annotation handling after actualEnterAnnotations Summary: Move all repeating annotations code into Annotate, rework annotations pipeline into a more completer-like design, eliminate a cast from enterAnnotations/enterTypeAnnotations Reviewed-by: jjg, jfranck
langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/share/classes/com/sun/tools/javac/code/SymbolMetadata.java
langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Wed Apr 09 17:18:22 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Apr 03 20:28:23 2014 -0400
@@ -34,7 +34,6 @@
 import javax.tools.JavaFileObject;
 
 import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.comp.Annotate;
 import com.sun.tools.javac.comp.Attr;
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Env;
@@ -153,10 +152,6 @@
         }
     }
 
-    public void appendTypeAttributesWithCompletion(final Annotate.AnnotateRepeatedContext<Attribute.TypeCompound> ctx) {
-        initedMetadata().appendTypeAttributesWithCompletion(ctx);
-    }
-
     public void appendUniqueTypeAttributes(List<Attribute.TypeCompound> l) {
         if (l.nonEmpty()) {
             initedMetadata().appendUniqueTypes(l);
@@ -211,10 +206,6 @@
         }
     }
 
-    public void setDeclarationAttributesWithCompletion(final Annotate.AnnotateRepeatedContext<Attribute.Compound> ctx) {
-        initedMetadata().setDeclarationAttributesWithCompletion(ctx);
-    }
-
     public void setTypeAttributes(List<Attribute.TypeCompound> a) {
         if (metadata != null || a.nonEmpty()) {
             if (metadata == null)
--- a/langtools/src/share/classes/com/sun/tools/javac/code/SymbolMetadata.java	Wed Apr 09 17:18:22 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/SymbolMetadata.java	Thu Apr 03 20:28:23 2014 -0400
@@ -25,19 +25,9 @@
 
 package com.sun.tools.javac.code;
 
-import java.util.Map;
 
-import javax.tools.JavaFileObject;
-
-import com.sun.tools.javac.comp.Annotate;
-import com.sun.tools.javac.comp.AttrContext;
-import com.sun.tools.javac.comp.Env;
-import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.Assert;
 import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.Log;
-import com.sun.tools.javac.util.Pair;
-import static com.sun.tools.javac.code.Kinds.PCK;
 
 /**
  * Container for all annotations (attributes in javac) on a Symbol.
@@ -157,70 +147,6 @@
         setClassInitTypeAttributes(other.getClassInitTypeAttributes());
     }
 
-    public void setDeclarationAttributesWithCompletion(final Annotate.AnnotateRepeatedContext<Attribute.Compound> ctx) {
-        Assert.check(pendingCompletion() || (!isStarted() && sym.kind == PCK));
-        this.setDeclarationAttributes(getAttributesForCompletion(ctx));
-    }
-
-    public void appendTypeAttributesWithCompletion(final Annotate.AnnotateRepeatedContext<Attribute.TypeCompound> ctx) {
-        this.appendUniqueTypes(getAttributesForCompletion(ctx));
-    }
-
-    private <T extends Attribute.Compound> List<T> getAttributesForCompletion(
-            final Annotate.AnnotateRepeatedContext<T> ctx) {
-
-        Map<Symbol.TypeSymbol, ListBuffer<T>> annotated = ctx.annotated;
-        boolean atLeastOneRepeated = false;
-        List<T> buf = List.<T>nil();
-        for (ListBuffer<T> lb : annotated.values()) {
-            if (lb.size() == 1) {
-                buf = buf.prepend(lb.first());
-            } else { // repeated
-                // This will break when other subtypes of Attributs.Compound
-                // are introduced, because PlaceHolder is a subtype of TypeCompound.
-                T res;
-                @SuppressWarnings("unchecked")
-                T ph = (T) new Placeholder<>(ctx, lb.toList(), sym);
-                res = ph;
-                buf = buf.prepend(res);
-                atLeastOneRepeated = true;
-            }
-        }
-
-        if (atLeastOneRepeated) {
-            // The Symbol s is now annotated with a combination of
-            // finished non-repeating annotations and placeholders for
-            // repeating annotations.
-            //
-            // We need to do this in two passes because when creating
-            // a container for a repeating annotation we must
-            // guarantee that the @Repeatable on the
-            // contained annotation is fully annotated
-            //
-            // The way we force this order is to do all repeating
-            // annotations in a pass after all non-repeating are
-            // finished. This will work because @Repeatable
-            // is non-repeating and therefore will be annotated in the
-            // fist pass.
-
-            // Queue a pass that will replace Attribute.Placeholders
-            // with Attribute.Compound (made from synthesized containers).
-            ctx.annotateRepeated(new Annotate.Worker() {
-                @Override
-                public String toString() {
-                    return "repeated annotation pass of: " + sym + " in: " + sym.owner;
-                }
-
-                @Override
-                public void run() {
-                    complete(ctx);
-                }
-            });
-        }
-        // Add non-repeating attributes
-        return buf.reverse();
-    }
-
     public SymbolMetadata reset() {
         attributes = DECL_IN_PROGRESS;
         return this;
@@ -313,143 +239,4 @@
     private boolean isStarted() {
         return attributes != DECL_NOT_STARTED;
     }
-
-    private List<Attribute.Compound> getPlaceholders() {
-        List<Attribute.Compound> res = List.<Attribute.Compound>nil();
-        for (Attribute.Compound a : filterDeclSentinels(attributes)) {
-            if (a instanceof Placeholder) {
-                res = res.prepend(a);
-            }
-        }
-        return res.reverse();
-    }
-
-    private List<Attribute.TypeCompound> getTypePlaceholders() {
-        List<Attribute.TypeCompound> res = List.<Attribute.TypeCompound>nil();
-        for (Attribute.TypeCompound a : type_attributes) {
-            if (a instanceof Placeholder) {
-                res = res.prepend(a);
-            }
-        }
-        return res.reverse();
-    }
-
-    /*
-     * Replace Placeholders for repeating annotations with their containers
-     */
-    private <T extends Attribute.Compound> void complete(Annotate.AnnotateRepeatedContext<T> ctx) {
-        Log log = ctx.log;
-        Env<AttrContext> env = ctx.env;
-        JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile);
-        try {
-            // TODO: can we reduce duplication in the following branches?
-            if (ctx.isTypeCompound) {
-                Assert.check(!isTypesEmpty());
-
-                if (isTypesEmpty()) {
-                    return;
-                }
-
-                List<Attribute.TypeCompound> result = List.nil();
-                for (Attribute.TypeCompound a : getTypeAttributes()) {
-                    if (a instanceof Placeholder) {
-                        @SuppressWarnings("unchecked")
-                        Placeholder<Attribute.TypeCompound> ph = (Placeholder<Attribute.TypeCompound>) a;
-                        Attribute.TypeCompound replacement = replaceOne(ph, ph.getRepeatedContext());
-
-                        if (null != replacement) {
-                            result = result.prepend(replacement);
-                        }
-                    } else {
-                        result = result.prepend(a);
-                    }
-                }
-
-                type_attributes = result.reverse();
-
-                Assert.check(SymbolMetadata.this.getTypePlaceholders().isEmpty());
-            } else {
-                Assert.check(!pendingCompletion());
-
-                if (isEmpty()) {
-                    return;
-                }
-
-                List<Attribute.Compound> result = List.nil();
-                for (Attribute.Compound a : getDeclarationAttributes()) {
-                    if (a instanceof Placeholder) {
-                        @SuppressWarnings("unchecked")
-                        Attribute.Compound replacement = replaceOne((Placeholder<T>) a, ctx);
-
-                        if (null != replacement) {
-                            result = result.prepend(replacement);
-                        }
-                    } else {
-                        result = result.prepend(a);
-                    }
-                }
-
-                attributes = result.reverse();
-
-                Assert.check(SymbolMetadata.this.getPlaceholders().isEmpty());
-            }
-        } finally {
-            log.useSource(oldSource);
-        }
-    }
-
-    private <T extends Attribute.Compound> T replaceOne(Placeholder<T> placeholder, Annotate.AnnotateRepeatedContext<T> ctx) {
-        Log log = ctx.log;
-
-        // Process repeated annotations
-        T validRepeated = ctx.processRepeatedAnnotations(placeholder.getPlaceholderFor(), sym);
-
-        if (validRepeated != null) {
-            // Check that the container isn't manually
-            // present along with repeated instances of
-            // its contained annotation.
-            ListBuffer<T> manualContainer = ctx.annotated.get(validRepeated.type.tsym);
-            if (manualContainer != null) {
-                log.error(ctx.pos.get(manualContainer.first()), "invalid.repeatable.annotation.repeated.and.container.present",
-                        manualContainer.first().type.tsym);
-            }
-        }
-
-        // A null return will delete the Placeholder
-        return validRepeated;
-    }
-
-    private static class Placeholder<T extends Attribute.Compound> extends Attribute.TypeCompound {
-
-        private final Annotate.AnnotateRepeatedContext<T> ctx;
-        private final List<T> placeholderFor;
-        private final Symbol on;
-
-        public Placeholder(Annotate.AnnotateRepeatedContext<T> ctx,
-                           List<T> placeholderFor, Symbol on) {
-            super(on.type, List.<Pair<Symbol.MethodSymbol, Attribute>>nil(),
-                  ctx.isTypeCompound ?
-                  ((Attribute.TypeCompound)placeholderFor.head).position :
-                  // TODO: Eventually, we will need to get rid of this
-                  // use of unknown, either by using null, or by
-                  // throwing an assertion failure here.
-                  TypeAnnotationPosition.unknown);
-            this.ctx = ctx;
-            this.placeholderFor = placeholderFor;
-            this.on = on;
-        }
-
-        @Override
-        public String toString() {
-            return "<placeholder: " + placeholderFor + " on: " + on + ">";
-        }
-
-        public List<T> getPlaceholderFor() {
-            return placeholderFor;
-        }
-
-        public Annotate.AnnotateRepeatedContext<T> getRepeatedContext() {
-            return ctx;
-        }
-    }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Wed Apr 09 17:18:22 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Thu Apr 03 20:28:23 2014 -0400
@@ -189,53 +189,72 @@
      * This context contains all the information needed to synthesize new
      * annotations trees by the completer for repeating annotations.
      */
-    public class AnnotateRepeatedContext<T extends Attribute.Compound> {
+    private class AnnotationContext<T extends Attribute.Compound> {
         public final Env<AttrContext> env;
         public final Map<Symbol.TypeSymbol, ListBuffer<T>> annotated;
         public final Map<T, JCDiagnostic.DiagnosticPosition> pos;
-        public final Log log;
         public final boolean isTypeCompound;
 
-        public AnnotateRepeatedContext(Env<AttrContext> env,
-                                       Map<Symbol.TypeSymbol, ListBuffer<T>> annotated,
-                                       Map<T, JCDiagnostic.DiagnosticPosition> pos,
-                                       Log log,
-                                       boolean isTypeCompound) {
+        public AnnotationContext(Env<AttrContext> env,
+                                 Map<Symbol.TypeSymbol, ListBuffer<T>> annotated,
+                                 Map<T, JCDiagnostic.DiagnosticPosition> pos,
+                                 boolean isTypeCompound) {
             Assert.checkNonNull(env);
             Assert.checkNonNull(annotated);
             Assert.checkNonNull(pos);
-            Assert.checkNonNull(log);
 
             this.env = env;
             this.annotated = annotated;
             this.pos = pos;
-            this.log = log;
             this.isTypeCompound = isTypeCompound;
         }
 
-        /**
-         * Process a list of repeating annotations returning a new
-         * Attribute.Compound that is the attribute for the synthesized tree
-         * for the container.
-         *
-         * @param repeatingAnnotations a List of repeating annotations
-         * @return a new Attribute.Compound that is the container for the repeatingAnnotations
-         */
-        public T processRepeatedAnnotations(List<T> repeatingAnnotations, Symbol sym) {
-            return Annotate.this.processRepeatedAnnotations(repeatingAnnotations, this, sym);
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("RepeatedContext[");
+            for (Map.Entry<Symbol.TypeSymbol, ListBuffer<T>> entry :
+                     annotated.entrySet()) {
+                sb.append(" ");
+                sb.append(entry.getKey());
+                sb.append(" = { ");
+                sb.append(entry.getValue());
+                sb.append(" }");
+            }
+            sb.append(" ]");
+            return sb.toString();
+        }
+    }
+
+    private static class Placeholder<T extends Attribute.Compound> extends Attribute.Compound {
+
+        private final Annotate.AnnotationContext<T> ctx;
+        private final List<T> placeholderFor;
+        private final Symbol on;
+
+        public Placeholder(Annotate.AnnotationContext<T> ctx,
+                           List<T> placeholderFor, Symbol on) {
+            super(on.type, List.<Pair<Symbol.MethodSymbol, Attribute>>nil(),
+                  placeholderFor.head.position);
+            this.ctx = ctx;
+            this.placeholderFor = placeholderFor;
+            this.on = on;
         }
 
-        /**
-         * Queue the Worker a on the repeating annotations queue of the
-         * Annotate instance this context belongs to.
-         *
-         * @param a the Worker to enqueue for repeating annotation annotating
-         */
-        public void annotateRepeated(Worker a) {
-            Annotate.this.repeated(a);
+        @Override
+        public String toString() {
+            return "<placeholder: " + placeholderFor + " on: " + on + ">";
+    }
+
+        public List<T> getPlaceholderFor() {
+            return placeholderFor;
+        }
+
+        public Annotate.AnnotationContext<T> getRepeatedContext() {
+            return ctx;
         }
     }
 
+
 /* ********************************************************************
  * Compute an attribute from its annotation.
  *********************************************************************/
@@ -247,24 +266,44 @@
     Attribute.Compound enterAnnotation(JCAnnotation a,
                                        Type expected,
                                        Env<AttrContext> env) {
-        return enterAnnotation(a, expected, env, false);
+        List<Pair<MethodSymbol,Attribute>> elems =
+            enterAttributeValues(a, expected, env);
+        Attribute.Compound ac = new Attribute.Compound(a.type, elems);
+        a.attribute = ac;
+
+        return ac;
     }
 
     Attribute.TypeCompound enterTypeAnnotation(JCAnnotation a,
-            Type expected,
-            Env<AttrContext> env) {
-        return (Attribute.TypeCompound) enterAnnotation(a, expected, env, true);
+                                               Type expected,
+                                               Env<AttrContext> env) {
+        List<Pair<MethodSymbol,Attribute>> elems =
+            enterAttributeValues(a, expected, env);
+
+        if (a.attribute == null || !(a.attribute instanceof Attribute.TypeCompound)) {
+            // Create a new TypeCompound
+
+            Attribute.TypeCompound tc =
+                new Attribute.TypeCompound(a.type, elems,
+                // TODO: Eventually, we will get rid of this use of
+                // unknown, because we'll get a position from
+                // MemberEnter (task 8027262).
+                                           TypeAnnotationPosition.unknown);
+            a.attribute = tc;
+            return tc;
+        } else {
+            // Use an existing TypeCompound
+            return (Attribute.TypeCompound)a.attribute;
+        }
     }
 
-    // boolean typeAnnotation determines whether the method returns
-    // a Compound (false) or TypeCompound (true).
-    Attribute.Compound enterAnnotation(JCAnnotation a,
-            Type expected,
-            Env<AttrContext> env,
-            boolean typeAnnotation) {
-        // The annotation might have had its type attributed (but not checked)
-        // by attr.attribAnnotationTypes during MemberEnter, in which case we do not
-        // need to do it again.
+    private List<Pair<MethodSymbol,Attribute>>
+            enterAttributeValues(JCAnnotation a,
+                                 Type expected,
+                                 Env<AttrContext> env) {
+        // The annotation might have had its type attributed (but not
+        // checked) by attr.attribAnnotationTypes during MemberEnter,
+        // in which case we do not need to do it again.
         Type at = (a.annotationType.type != null ? a.annotationType.type
                   : attr.attribType(a.annotationType, env));
         a.type = chk.checkType(a.annotationType.pos(), at, expected);
@@ -312,27 +351,7 @@
                 buf.append(new Pair<>((MethodSymbol)method, value));
             t.type = result;
         }
-        if (typeAnnotation) {
-            if (a.attribute == null || !(a.attribute instanceof Attribute.TypeCompound)) {
-                // Create a new TypeCompound
-
-                Attribute.TypeCompound tc =
-                    new Attribute.TypeCompound(a.type, buf.toList(),
-                // TODO: Eventually, we will get rid of this use of
-                // unknown, because we'll get a position from
-                // MemberEnter (task 8027262).
-                                               TypeAnnotationPosition.unknown);
-                a.attribute = tc;
-                return tc;
-            } else {
-                // Use an existing TypeCompound
-                return a.attribute;
-            }
-        } else {
-            Attribute.Compound ac = new Attribute.Compound(a.type, buf.toList());
-            a.attribute = ac;
-            return ac;
-        }
+        return buf.toList();
     }
 
     Attribute enterAttributeValue(Type expected,
@@ -459,7 +478,7 @@
      * annotation are invalid.  This method reports errors/warnings.
      */
     private <T extends Attribute.Compound> T processRepeatedAnnotations(List<T> annotations,
-            AnnotateRepeatedContext<T> ctx,
+            AnnotationContext<T> ctx,
             Symbol on) {
         T firstOccurrence = annotations.head;
         List<Attribute> repeated = List.nil();
@@ -686,6 +705,172 @@
         return fatalError ? null : containerValueSymbol;
     }
 
+    private <T extends Attribute.Compound> AnnotationContext<T>
+            prepareEnterAnnotations(List<JCAnnotation> annotations,
+                                    Env<AttrContext> env,
+                                    Symbol sym,
+                                    AttributeCreator<T> creator,
+                                    boolean isTypeCompound) {
+        Map<TypeSymbol, ListBuffer<T>> annotated = new LinkedHashMap<>();
+        Map<T, DiagnosticPosition> pos = new HashMap<>();
+
+        for (List<JCAnnotation> al = annotations; !al.isEmpty(); al = al.tail) {
+            JCAnnotation a = al.head;
+            T c = creator.create(a, syms.annotationType, env);
+
+            Assert.checkNonNull(c, "Failed to create annotation");
+
+            if (annotated.containsKey(a.type.tsym)) {
+                if (!allowRepeatedAnnos) {
+                    log.error(a.pos(), "repeatable.annotations.not.supported.in.source");
+                    allowRepeatedAnnos = true;
+                }
+                ListBuffer<T> l = annotated.get(a.type.tsym);
+                l = l.append(c);
+                annotated.put(a.type.tsym, l);
+                pos.put(c, a.pos());
+            } else {
+                annotated.put(a.type.tsym, ListBuffer.of(c));
+                pos.put(c, a.pos());
+            }
+
+            // Note: @Deprecated has no effect on local variables and parameters
+            if (!c.type.isErroneous()
+                && sym.owner.kind != MTH
+                && types.isSameType(c.type, syms.deprecatedType)) {
+                sym.flags_field |= Flags.DEPRECATED;
+            }
+        }
+
+        return new AnnotationContext<>(env, annotated, pos,
+                                             isTypeCompound);
+    }
+
+    // Gather up annotations into a map from type symbols to lists of
+    // Compound attributes, then continue on with repeating
+    // annotations processing
+    private <T extends Attribute.Compound>
+            void attachAttributesLater(final List<JCAnnotation> annotations,
+                                       final Env<AttrContext> env,
+                                       final Symbol sym,
+                                       final boolean isTypeCompound,
+                                       final AttributeCreator<T> creator,
+                                       final AttributeAttacher<T> attacher) {
+        final AnnotationContext<T> ctx =
+            prepareEnterAnnotations(annotations, env, sym, creator, isTypeCompound);
+        final Map<Symbol.TypeSymbol, ListBuffer<T>> annotated =
+            ctx.annotated;
+        boolean hasRepeated = false;
+
+        List<T> buf = List.<T>nil();
+        for (ListBuffer<T> lb : annotated.values()) {
+            if (lb.size() == 1) {
+                buf = buf.prepend(lb.first());
+            } else {
+                @SuppressWarnings("unchecked")
+                T res = (T) new Placeholder<>(ctx, lb.toList(), sym);
+                buf = buf.prepend(res);
+                hasRepeated = true;
+            }
+        }
+
+        final List<T> attrs = buf.reverse();
+
+        if (!isTypeCompound) {
+            // Attach declaration attributes early, so
+            // that @Repeatable and other annotations get attached.
+            // Since the attacher uses setDeclarationAttributes, this
+            // will be overwritten later.
+            attacher.attach(sym, attrs);
+        }
+        if (hasRepeated) {
+            repeated(new Annotate.Worker() {
+                    @Override
+                    public String toString() {
+                        return "repeated annotation pass of: " + sym + " in: " + sym.owner;
+                    }
+
+                    @Override
+                    public void run() {
+                        JavaFileObject oldSource =
+                            log.useSource(env.toplevel.sourcefile);
+                        try {
+                            attacher.attach(sym, replacePlaceholders(attrs, ctx, sym));
+                        } finally {
+                            log.useSource(oldSource);
+                        }
+                    }
+                });
+        } else {
+            attacher.attach(sym, attrs);
+        }
+    }
+
+    private interface AttributeAttacher<T extends Attribute.Compound> {
+        public void attach(Symbol sym, List<T> attrs);
+    }
+
+    private final AttributeAttacher<Attribute.Compound> declAnnotationsAttacher =
+        new AttributeAttacher<Attribute.Compound>() {
+            @Override
+            public void attach(Symbol sym, List<Attribute.Compound> attrs) {
+                sym.resetAnnotations();
+                sym.setDeclarationAttributes(attrs);
+            }
+        };
+
+    private final AttributeAttacher<Attribute.TypeCompound> typeAnnotationsAttacher =
+        new AttributeAttacher<Attribute.TypeCompound>() {
+            @Override
+            public void attach(Symbol sym, List<Attribute.TypeCompound> attrs) {
+                sym.appendUniqueTypeAttributes(attrs);
+            }
+        };
+
+    private <T extends Attribute.Compound> List<T>
+            replacePlaceholders(List<T> buf,
+                                Annotate.AnnotationContext<T> ctx,
+                                Symbol sym) {
+        List<T> result = List.nil();
+        for (T a : buf) {
+            if (a instanceof Placeholder) {
+                @SuppressWarnings("unchecked")
+                    T replacement = replaceOne((Placeholder<T>) a, ctx, sym);
+
+                if (null != replacement) {
+                    result = result.prepend(replacement);
+                }
+            } else {
+                result = result.prepend(a);
+            }
+        }
+
+        return result.reverse();
+    }
+
+    private <T extends Attribute.Compound> T replaceOne(Placeholder<T> placeholder,
+                                                        Annotate.AnnotationContext<T> ctx,
+                                                        Symbol sym) {
+        // Process repeated annotations
+        T validRepeated =
+            processRepeatedAnnotations(placeholder.getPlaceholderFor(),
+                                       ctx, sym);
+
+        if (validRepeated != null) {
+            // Check that the container isn't manually
+            // present along with repeated instances of
+            // its contained annotation.
+            ListBuffer<T> manualContainer = ctx.annotated.get(validRepeated.type.tsym);
+            if (manualContainer != null) {
+                log.error(ctx.pos.get(manualContainer.first()), "invalid.repeatable.annotation.repeated.and.container.present",
+                        manualContainer.first().type.tsym);
+            }
+        }
+
+        // A null return will delete the Placeholder
+        return validRepeated;
+    }
+
 /* ********************************************************************
  * Annotation processing
  *********************************************************************/
@@ -745,44 +930,39 @@
         });
     }
 
+    private interface AttributeCreator<T extends Attribute.Compound> {
+        public T create(JCAnnotation a, Type expected, Env<AttrContext> env);
+    }
+
+    // TODO: When SE8 features can be used, these can go away and be
+    // replaced by method refs.
+    private final AttributeCreator<Attribute.Compound> enterAnnotationsCreator =
+        new AttributeCreator<Attribute.Compound>() {
+        @Override
+        public Attribute.Compound create(JCAnnotation a,
+                                         Type expected,
+                                         Env<AttrContext> env) {
+            return enterAnnotation(a, syms.annotationType, env);
+        }
+    };
+    private final AttributeCreator<Attribute.TypeCompound> enterTypeAnnotationsCreator =
+        new AttributeCreator<Attribute.TypeCompound>() {
+        @Override
+        public Attribute.TypeCompound create(JCAnnotation a,
+                                             Type expected,
+                                             Env<AttrContext> env) {
+            return enterTypeAnnotation(a, syms.annotationType, env);
+        }
+    };
+
     /** Enter a set of annotations. */
     private void actualEnterAnnotations(List<JCAnnotation> annotations,
                                         Env<AttrContext> env,
                                         Symbol s) {
-        Map<TypeSymbol, ListBuffer<Attribute.Compound>> annotated = new LinkedHashMap<>();
-        Map<Attribute.Compound, DiagnosticPosition> pos = new HashMap<>();
-
-        for (List<JCAnnotation> al = annotations; !al.isEmpty(); al = al.tail) {
-            JCAnnotation a = al.head;
-            Attribute.Compound c = enterAnnotation(a, syms.annotationType, env);
-            if (c == null) {
-                continue;
-            }
-
-            if (annotated.containsKey(a.type.tsym)) {
-                if (!allowRepeatedAnnos) {
-                    log.error(a.pos(), "repeatable.annotations.not.supported.in.source");
-                    allowRepeatedAnnos = true;
-                }
-                ListBuffer<Attribute.Compound> l = annotated.get(a.type.tsym);
-                l = l.append(c);
-                annotated.put(a.type.tsym, l);
-                pos.put(c, a.pos());
-            } else {
-                annotated.put(a.type.tsym, ListBuffer.of(c));
-                pos.put(c, a.pos());
-            }
-
-            // Note: @Deprecated has no effect on local variables and parameters
-            if (!c.type.isErroneous()
-                && s.owner.kind != MTH
-                && types.isSameType(c.type, syms.deprecatedType)) {
-                s.flags_field |= Flags.DEPRECATED;
-            }
-        }
-
-        s.setDeclarationAttributesWithCompletion(
-            new AnnotateRepeatedContext<>(env, annotated, pos, log, false));
+        Assert.checkNonNull(s, "Symbol argument to actualEnterAnnotations is null");
+        attachAttributesLater(annotations, env, s, false,
+                              enterAnnotationsCreator,
+                              declAnnotationsAttacher);
     }
 
     /*
@@ -792,9 +972,7 @@
                                             final Env<AttrContext> env,
                                             final Symbol s,
                                             final DiagnosticPosition deferPos) {
-        Map<TypeSymbol, ListBuffer<Attribute.TypeCompound>> annotated = new LinkedHashMap<>();
-        Map<Attribute.TypeCompound, DiagnosticPosition> pos = new HashMap<>();
-
+        Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is nul/");
         JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
         DiagnosticPosition prevLintPos = null;
 
@@ -802,32 +980,9 @@
             prevLintPos = deferredLintHandler.setPos(deferPos);
         }
         try {
-
-            for (List<JCAnnotation> al = annotations; !al.isEmpty(); al = al.tail) {
-                JCAnnotation a = al.head;
-                Attribute.TypeCompound tc =
-                    enterTypeAnnotation(a, syms.annotationType, env);
-
-                Assert.checkNonNull(tc, "Failed to create type annotation");
-
-                if (annotated.containsKey(a.type.tsym)) {
-                    if (!allowRepeatedAnnos) {
-                        log.error(a.pos(), "repeatable.annotations.not.supported.in.source");
-                        allowRepeatedAnnos = true;
-                    }
-                    ListBuffer<Attribute.TypeCompound> l = annotated.get(a.type.tsym);
-                    l = l.append(tc);
-                    annotated.put(a.type.tsym, l);
-                    pos.put(tc, a.pos());
-                } else {
-                    annotated.put(a.type.tsym, ListBuffer.of(tc));
-                    pos.put(tc, a.pos());
-                }
-            }
-
-            Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is null");
-            s.appendTypeAttributesWithCompletion(
-                new AnnotateRepeatedContext<>(env, annotated, pos, log, true));
+            attachAttributesLater(annotations, env, s, true,
+                                  enterTypeAnnotationsCreator,
+                                  typeAnnotationsAttacher);
         } finally {
             if (prevLintPos != null)
                 deferredLintHandler.setPos(prevLintPos);