Disambiguate type annotations on fields, parameters, and methods

Given a declaration like `private @A int x;` or `@A private int x;`,
there are three possibilities:

* `@A` is a declaration annotation on the field
* `@A` is a `TYPE_USE` annotation on the type
* `@A` sets `TYPE_USE` _and_ `FIELD` targets, and appears in the
  bytecode as both a declaration annotation and as a type annotation

This can't be determined syntactically -- note that the presence of
other modifiers before or after the annotation has no bearing on whether
the annotation targets the type or the declaration.

So, we wait until constant binding is done, read the `@Target`
meta-annotation for each ambiguous annotation, and move it to the
appropriate location.

MOE_MIGRATED_REVID=137749757
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index fd064e8..07264e3 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -99,10 +99,13 @@
             syms, henv, CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv));
 
     tenv =
-        canonicalizeTypes(
+        constants(
             syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
     tenv =
-        constants(
+        disambiguateTypeAnnotations(
+            syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
+    tenv =
+        canonicalizeTypes(
             syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
 
     ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder();
@@ -309,6 +312,21 @@
     return builder.build();
   }
 
+  /**
+   * Disambiguate annotations on field types and method return types that could be declaration or
+   * type annotations.
+   */
+  private static Env<ClassSymbol, SourceTypeBoundClass> disambiguateTypeAnnotations(
+      ImmutableSet<ClassSymbol> syms,
+      Env<ClassSymbol, SourceTypeBoundClass> stenv,
+      Env<ClassSymbol, TypeBoundClass> tenv) {
+    SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
+    for (ClassSymbol sym : syms) {
+      builder.putIfAbsent(sym, DisambiguateTypeAnnotations.bind(stenv.get(sym), tenv));
+    }
+    return builder.build();
+  }
+
   /** The result of binding: bound nodes for sources in the compilation, and the classpath. */
   public static class BindingResult {
     private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units;
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index 4a3fc55..f9eea46 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -66,7 +66,8 @@
         base.typeParameters(),
         base.scope(),
         base.memberImports(),
-        base.retention(),
+        base.annotationRetention(),
+        base.annotationTarget(),
         base.annotations(),
         base.source());
   }
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index daba4b9..fc88824 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.turbine.binder.bound.EnumConstantValue;
 import com.google.turbine.binder.bound.SourceTypeBoundClass;
 import com.google.turbine.binder.bound.TypeBoundClass;
@@ -30,6 +31,8 @@
 import com.google.turbine.binder.sym.FieldSymbol;
 import com.google.turbine.binder.sym.TyVarSymbol;
 import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.Const.Kind;
 import com.google.turbine.model.Const.Value;
 import com.google.turbine.model.TurbineFlag;
 import com.google.turbine.model.TurbineTyKind;
@@ -43,7 +46,9 @@
 import com.google.turbine.type.Type.WildTy;
 import com.google.turbine.type.Type.WildUnboundedTy;
 import com.google.turbine.type.Type.WildUpperBoundedTy;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.RetentionPolicy;
+import java.util.EnumSet;
 import java.util.Map;
 import javax.annotation.Nullable;
 
@@ -84,6 +89,7 @@
         base.scope(),
         base.memberImports(),
         bindRetention(base.kind(), annos),
+        bindTarget(base.kind(), annos),
         annos,
         base.source());
   }
@@ -146,6 +152,53 @@
     return RetentionPolicy.CLASS;
   }
 
+  /** Returns the target {@link ElementType}s for an annotation declaration, or {@code null}. */
+  @Nullable
+  static ImmutableSet<ElementType> bindTarget(TurbineTyKind kind, Iterable<AnnoInfo> annotations) {
+    if (kind != TurbineTyKind.ANNOTATION) {
+      return null;
+    }
+    for (AnnoInfo annotation : annotations) {
+      switch (annotation.sym().binaryName()) {
+        case "java/lang/annotation/Target":
+          return bindTarget(annotation);
+        default:
+          break;
+      }
+    }
+    EnumSet<ElementType> target = EnumSet.allOf(ElementType.class);
+    target.remove(ElementType.TYPE_USE);
+    target.remove(ElementType.TYPE_PARAMETER);
+    return ImmutableSet.copyOf(target);
+  }
+
+  private static ImmutableSet<ElementType> bindTarget(AnnoInfo annotation) {
+    ImmutableSet.Builder<ElementType> result = ImmutableSet.builder();
+    Const val = annotation.values().get("value");
+    switch (val.kind()) {
+      case ARRAY:
+        for (Const element : ((ArrayInitValue) val).elements()) {
+          if (element.kind() == Kind.ENUM_CONSTANT) {
+            bindTargetElement(result, (EnumConstantValue) element);
+          }
+        }
+        break;
+      case ENUM_CONSTANT:
+        bindTargetElement(result, (EnumConstantValue) val);
+        break;
+      default:
+        break;
+    }
+    return result.build();
+  }
+
+  private static void bindTargetElement(
+      ImmutableSet.Builder<ElementType> target, EnumConstantValue enumVal) {
+    if (enumVal.sym().owner().binaryName().equals("java/lang/annotation/ElementType")) {
+      target.add(ElementType.valueOf(enumVal.sym().name()));
+    }
+  }
+
   private ImmutableList<TypeBoundClass.FieldInfo> fields(
       ImmutableList<TypeBoundClass.FieldInfo> fields) {
     ImmutableList.Builder<TypeBoundClass.FieldInfo> result = ImmutableList.builder();
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index c90ea3b..eeacbbf 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -143,7 +143,7 @@
   Const evalClassLiteral(ClassLiteral t) {
     switch (t.type().kind()) {
       case PRIM_TY:
-        return new ClassValue(new Type.PrimTy(((PrimTy) t.type()).tykind()));
+        return new ClassValue(new Type.PrimTy(((PrimTy) t.type()).tykind(), ImmutableList.of()));
       case VOID_TY:
         return new ClassValue(Type.VOID);
       case CLASS_TY:
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
new file mode 100644
index 0000000..bb4cb09
--- /dev/null
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.binder;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyVar;
+import java.lang.annotation.ElementType;
+import java.util.Set;
+
+/**
+ * Disambiguate annotations on field, parameter, and method return types that could be declaration
+ * or type annotations.
+ *
+ * <p>Given a declaration like {@code private @A int x;} or {@code @A private int x;}, there are
+ * three possibilities:
+ *
+ * <ol>
+ *   <li>{@code @A} is a declaration annotation on the field.
+ *   <li>{@code @A} is a {@code TYPE_USE} annotation on the type.
+ *   <li>{@code @A} sets {@code TYPE_USE} <em>and</em> {@code FIELD} targets, and appears in the
+ *       bytecode as both a declaration annotation and as a type annotation.
+ * </ol>
+ *
+ * <p>This can't be disambiguated syntactically (note that the presence of other modifiers before or
+ * after the annotation has no bearing on whether it's a type annotation). So, we wait until
+ * constant binding is done, read the {@code @Target} meta-annotation for each ambiguous annotation,
+ * and move it to the appropriate location.
+ */
+public class DisambiguateTypeAnnotations {
+  public static SourceTypeBoundClass bind(
+      SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) {
+    return new SourceTypeBoundClass(
+        base.interfaceTypes(),
+        base.superClassType(),
+        base.typeParameterTypes(),
+        base.access(),
+        bindMethods(env, base.methods()),
+        bindFields(env, base.fields()),
+        base.owner(),
+        base.kind(),
+        base.children(),
+        base.superclass(),
+        base.interfaces(),
+        base.typeParameters(),
+        base.scope(),
+        base.memberImports(),
+        base.annotationRetention(),
+        base.annotationTarget(),
+        base.annotations(),
+        base.source());
+  }
+
+  private static ImmutableList<MethodInfo> bindMethods(
+      Env<ClassSymbol, TypeBoundClass> env, ImmutableList<MethodInfo> fields) {
+    ImmutableList.Builder<MethodInfo> result = ImmutableList.builder();
+    for (MethodInfo field : fields) {
+      result.add(bindMethod(env, field));
+    }
+    return result.build();
+  }
+
+  private static MethodInfo bindMethod(Env<ClassSymbol, TypeBoundClass> env, MethodInfo base) {
+    ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
+    Type returnType =
+        disambiguate(
+            env, ElementType.METHOD, base.returnType(), base.annotations(), declarationAnnotations);
+    return new MethodInfo(
+        base.sym(),
+        base.tyParams(),
+        returnType,
+        bindParameters(env, base.parameters()),
+        base.exceptions(),
+        base.access(),
+        base.defaultValue(),
+        base.decl(),
+        declarationAnnotations.build());
+  }
+
+  private static ImmutableList<ParamInfo> bindParameters(
+      Env<ClassSymbol, TypeBoundClass> env, ImmutableList<ParamInfo> params) {
+    ImmutableList.Builder<ParamInfo> result = ImmutableList.builder();
+    for (ParamInfo param : params) {
+      result.add(bindParam(env, param));
+    }
+    return result.build();
+  }
+
+  private static ParamInfo bindParam(Env<ClassSymbol, TypeBoundClass> env, ParamInfo base) {
+    ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
+    Type type =
+        disambiguate(
+            env, ElementType.PARAMETER, base.type(), base.annotations(), declarationAnnotations);
+    return new ParamInfo(type, declarationAnnotations.build(), base.synthetic());
+  }
+
+  /**
+   * Moves type annotations in {@code annotations} to {@code type}, and adds any declaration
+   * annotations on {@code type} to {@code declarationAnnotations}.
+   */
+  private static Type disambiguate(
+      Env<ClassSymbol, TypeBoundClass> env,
+      ElementType declarationTarget,
+      Type type,
+      ImmutableList<AnnoInfo> annotations,
+      Builder<AnnoInfo> declarationAnnotations) {
+    ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder();
+    for (AnnoInfo anno : annotations) {
+      Set<ElementType> target = env.get(anno.sym()).annotationTarget();
+      if (target.contains(ElementType.TYPE_USE)) {
+        typeAnnotations.add(anno);
+      }
+      if (target.contains(declarationTarget)) {
+        declarationAnnotations.add(anno);
+      }
+    }
+    return fixAnnotations(env, type, typeAnnotations.build(), declarationAnnotations);
+  }
+
+  private static ImmutableList<FieldInfo> bindFields(
+      Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) {
+    ImmutableList.Builder<FieldInfo> result = ImmutableList.builder();
+    for (FieldInfo field : fields) {
+      result.add(bindField(env, field));
+    }
+    return result.build();
+  }
+
+  private static FieldInfo bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base) {
+    ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
+    Type type =
+        disambiguate(
+            env, ElementType.FIELD, base.type(), base.annotations(), declarationAnnotations);
+    return new FieldInfo(
+        base.sym(), type, base.access(), declarationAnnotations.build(), base.decl(), base.value());
+  }
+
+  /**
+   * Finds the left-most annotatable type in {@code type}, adds the {@code extra} type annotations
+   * to it, and removes any declaration annotations and saves them in {@code removed}.
+   *
+   * <p>The left-most type is e.g. the element type of an array, or the left-most type in a nested
+   * type declaration.
+   *
+   * <p>Note: the second case means that type annotation disambiguation has to occur on nested types
+   * before they are canonicalized.
+   */
+  private static Type fixAnnotations(
+      Env<ClassSymbol, TypeBoundClass> env,
+      Type type,
+      ImmutableList<AnnoInfo> extra,
+      Builder<AnnoInfo> removed) {
+    switch (type.tyKind()) {
+      case PRIM_TY:
+        PrimTy primTy = (PrimTy) type;
+        return new Type.PrimTy(
+            primTy.primkind(), fixAnnotations(env, primTy.annos(), extra, removed));
+      case CLASS_TY:
+        ClassTy classTy = (ClassTy) type;
+        SimpleClassTy base = classTy.classes.get(0);
+        SimpleClassTy simple =
+            new SimpleClassTy(
+                base.sym(), base.targs(), fixAnnotations(env, base.annos(), extra, removed));
+        return new Type.ClassTy(
+            ImmutableList.<SimpleClassTy>builder()
+                .add(simple)
+                .addAll(classTy.classes.subList(1, classTy.classes.size()))
+                .build());
+      case ARRAY_TY:
+        ArrayTy arrayTy = (ArrayTy) type;
+        return new ArrayTy(
+            fixAnnotations(env, arrayTy.elementType(), extra, removed), arrayTy.annos());
+      case TY_VAR:
+        TyVar tyVar = (TyVar) type;
+        return new Type.TyVar(tyVar.sym(), fixAnnotations(env, tyVar.annos(), extra, removed));
+      case VOID_TY:
+        return type;
+      case WILD_TY:
+        throw new AssertionError("unexpected wildcard type outside type argument context");
+      default:
+        throw new AssertionError(type.tyKind());
+    }
+  }
+
+  private static ImmutableList<AnnoInfo> fixAnnotations(
+      Env<ClassSymbol, TypeBoundClass> env,
+      ImmutableList<AnnoInfo> annos,
+      ImmutableList<AnnoInfo> extra,
+      Builder<AnnoInfo> removed) {
+    ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
+    for (AnnoInfo anno : annos) {
+      Set<ElementType> target = env.get(anno.sym()).annotationTarget();
+      if (target.contains(ElementType.TYPE_USE)) {
+        result.add(anno);
+      } else {
+        removed.add(anno);
+      }
+    }
+    result.addAll(extra);
+    return result.build();
+  }
+}
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index cf39b3d..397fef4 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Verify;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.turbine.binder.bound.HeaderBoundClass;
 import com.google.turbine.binder.bound.SourceHeaderBoundClass;
 import com.google.turbine.binder.bound.SourceTypeBoundClass;
@@ -43,6 +44,7 @@
 import com.google.turbine.model.TurbineVisibility;
 import com.google.turbine.tree.Tree;
 import com.google.turbine.tree.Tree.ClassTy;
+import com.google.turbine.tree.Tree.PrimTy;
 import com.google.turbine.tree.TurbineModifier;
 import com.google.turbine.type.AnnoInfo;
 import com.google.turbine.type.Type;
@@ -226,6 +228,7 @@
         scope,
         base.memberImports(),
         /*retention*/ null,
+        /*target*/ ImmutableSet.of(),
         annotations,
         base.source());
   }
@@ -282,7 +285,9 @@
               ImmutableList.of(
                   new ParamInfo(Type.ClassTy.STRING, ImmutableList.of(), true),
                   new ParamInfo(
-                      new Type.PrimTy(TurbineConstantTypeKind.INT), ImmutableList.of(), true)),
+                      new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()),
+                      ImmutableList.of(),
+                      true)),
               ImmutableList.of(),
               TurbineFlag.ACC_PRIVATE | TurbineFlag.ACC_SYNTH_CTOR,
               null,
@@ -404,7 +409,7 @@
         parameters.add(new ParamInfo(Type.ClassTy.STRING, ImmutableList.of(), /*synthetic*/ true));
         parameters.add(
             new ParamInfo(
-                new Type.PrimTy(TurbineConstantTypeKind.INT),
+                new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()),
                 ImmutableList.of(),
                 /*synthetic*/ true));
       }
@@ -527,7 +532,7 @@
       case CLASS_TY:
         return bindClassTy(scope, (Tree.ClassTy) t);
       case PRIM_TY:
-        return bindPrimTy((Tree.PrimTy) t);
+        return bindPrimTy(scope, (Tree.PrimTy) t);
       case ARR_TY:
         return bindArrTy(scope, (Tree.ArrTy) t);
       case VOID_TY:
@@ -594,8 +599,8 @@
     return new Type.ClassTy(classes.build());
   }
 
-  private static Type.PrimTy bindPrimTy(Tree.PrimTy t) {
-    return new Type.PrimTy(t.tykind());
+  private Type.PrimTy bindPrimTy(CompoundScope scope, PrimTy t) {
+    return new Type.PrimTy(t.tykind(), bindAnnotations(scope, t.annos()));
   }
 
   private Type bindArrTy(CompoundScope scope, Tree.ArrTy t) {
diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
index 1afc52d..6a468b2 100644
--- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.turbine.binder.lookup.CompoundScope;
 import com.google.turbine.binder.lookup.MemberImportIndex;
 import com.google.turbine.binder.sym.ClassSymbol;
@@ -27,6 +28,7 @@
 import com.google.turbine.type.AnnoInfo;
 import com.google.turbine.type.Type;
 import com.google.turbine.type.Type.ClassTy;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.RetentionPolicy;
 import javax.annotation.Nullable;
 
@@ -50,6 +52,7 @@
   private final CompoundScope scope;
   private final MemberImportIndex memberImports;
   private final RetentionPolicy retention;
+  private final ImmutableSet<ElementType> annotationTarget;
   private final ImmutableList<AnnoInfo> annotations;
   private final SourceFile source;
 
@@ -69,6 +72,7 @@
       CompoundScope scope,
       MemberImportIndex memberImports,
       RetentionPolicy retention,
+      ImmutableSet<ElementType> annotationTarget,
       ImmutableList<AnnoInfo> annotations,
       SourceFile source) {
     this.interfaceTypes = interfaceTypes;
@@ -86,6 +90,7 @@
     this.scope = scope;
     this.memberImports = memberImports;
     this.retention = retention;
+    this.annotationTarget = annotationTarget;
     this.annotations = annotations;
     this.source = source;
   }
@@ -144,10 +149,15 @@
   }
 
   @Override
-  public RetentionPolicy retention() {
+  public RetentionPolicy annotationRetention() {
     return retention;
   }
 
+  @Override
+  public ImmutableSet<ElementType> annotationTarget() {
+    return annotationTarget;
+  }
+
   /** Declared fields. */
   @Override
   public ImmutableList<FieldInfo> fields() {
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index 43c20da..6fddf64 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.turbine.binder.sym.FieldSymbol;
 import com.google.turbine.binder.sym.MethodSymbol;
 import com.google.turbine.binder.sym.TyVarSymbol;
@@ -26,6 +27,7 @@
 import com.google.turbine.tree.Tree.MethDecl;
 import com.google.turbine.type.AnnoInfo;
 import com.google.turbine.type.Type;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.RetentionPolicy;
 
 /** A bound node that augments {@link HeaderBoundClass} with type information. */
@@ -43,7 +45,10 @@
   ImmutableList<MethodInfo> methods();
 
   /** Retention policy for annotation declarations, {@code null} for other declarations. */
-  RetentionPolicy retention();
+  RetentionPolicy annotationRetention();
+
+  /** Target element types for annotation declarations, {@code null} for other declarations. */
+  ImmutableSet<ElementType> annotationTarget();
 
   /** A type parameter declaration. */
   class TyVarInfo {
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
index 9c2d86c..ca66046 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
@@ -20,6 +20,7 @@
 import com.google.turbine.binder.sym.ClassSymbol;
 import com.google.turbine.binder.sym.TyVarSymbol;
 import com.google.turbine.bytecode.sig.Sig;
+import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig;
 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig;
 import com.google.turbine.bytecode.sig.Sig.WildTySig;
 import com.google.turbine.type.Type;
@@ -58,7 +59,7 @@
         return new Type.WildUnboundedTy(ImmutableList.of());
       case LOWER:
         return new Type.WildLowerBoundedTy(
-            bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of());
+            bindTy(((LowerBoundTySig) sig).bound(), scope), ImmutableList.of());
       case UPPER:
         return new Type.WildUpperBoundedTy(
             bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of());
@@ -70,7 +71,7 @@
   static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) {
     switch (sig.kind()) {
       case BASE_TY_SIG:
-        return new Type.PrimTy(((Sig.BaseTySig) sig).type());
+        return new Type.PrimTy(((Sig.BaseTySig) sig).type(), ImmutableList.of());
       case CLASS_TY_SIG:
         return bindClassTy((Sig.ClassTySig) sig, scope);
       case TY_VAR_SIG:
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index 78bcf7d..a6efe01 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -23,6 +23,7 @@
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.turbine.binder.bound.BoundClass;
 import com.google.turbine.binder.bound.HeaderBoundClass;
 import com.google.turbine.binder.bound.TypeBoundClass;
@@ -32,7 +33,11 @@
 import com.google.turbine.binder.sym.MethodSymbol;
 import com.google.turbine.binder.sym.TyVarSymbol;
 import com.google.turbine.bytecode.ClassFile;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.Kind;
 import com.google.turbine.bytecode.ClassReader;
 import com.google.turbine.bytecode.sig.Sig;
 import com.google.turbine.bytecode.sig.Sig.ClassSig;
@@ -42,7 +47,9 @@
 import com.google.turbine.model.TurbineTyKind;
 import com.google.turbine.type.Type;
 import com.google.turbine.type.Type.ClassTy;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.RetentionPolicy;
+import java.util.EnumSet;
 import java.util.Map;
 import java.util.function.Function;
 import javax.annotation.Nullable;
@@ -408,10 +415,65 @@
           });
 
   @Override
-  public RetentionPolicy retention() {
+  public RetentionPolicy annotationRetention() {
     return retention.get();
   }
 
+  final Supplier<ImmutableSet<ElementType>> target =
+      Suppliers.memoize(
+          new Supplier<ImmutableSet<ElementType>>() {
+            @Override
+            public ImmutableSet<ElementType> get() {
+              if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
+                return null;
+              }
+              for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) {
+                switch (annotation.typeName()) {
+                  case "Ljava/lang/annotation/Target;":
+                    return bindTarget(annotation);
+                  default:
+                    break;
+                }
+              }
+              EnumSet<ElementType> target = EnumSet.allOf(ElementType.class);
+              target.remove(ElementType.TYPE_USE);
+              target.remove(ElementType.TYPE_PARAMETER);
+              return ImmutableSet.copyOf(target);
+            }
+          });
+
+  private static ImmutableSet<ElementType> bindTarget(AnnotationInfo annotation) {
+    ImmutableSet.Builder<ElementType> result = ImmutableSet.builder();
+    ElementValue val = annotation.elementValuePairs().get("value");
+    switch (val.kind()) {
+      case ARRAY:
+        for (ElementValue element : ((ArrayValue) val).elements()) {
+          if (element.kind() == Kind.ENUM) {
+            bindTargetElement(result, (EnumConstValue) element);
+          }
+        }
+        break;
+      case ENUM:
+        bindTargetElement(result, (EnumConstValue) val);
+        break;
+      default:
+        break;
+    }
+    return result.build();
+  }
+
+  private static void bindTargetElement(
+      ImmutableSet.Builder<ElementType> target, EnumConstValue enumVal) {
+    if (enumVal.typeName().equals("Ljava/lang/annotation/ElementType;")) {
+      target.add(ElementType.valueOf(enumVal.constName()));
+    }
+  }
+
+  @Override
+  public ImmutableSet<ElementType> annotationTarget() {
+    return target.get();
+  }
+
   /**
    * Create a scope for resolving type variable symbols declared in the class, and any enclosing
    * instances.
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index 48381ea..5311fdc 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -140,8 +140,8 @@
   /**
    * Processes a JVMS 4.7.16 RuntimeVisibleAnnotations attribute.
    *
-   * <p>The only annotation that affects header compilation is {@link @Retention} on annotation
-   * declarations.
+   * <p>The only annotations that affect header compilation are {@link @Retention} and
+   * {@link @Target} on annotation declarations.
    */
   private List<ClassFile.AnnotationInfo> readAnnotations(
       ConstantPoolReader constantPool, short accessFlags) {
@@ -162,19 +162,28 @@
   }
 
   /**
-   * Extracts an {@link @Retention} {@link ClassFile.AnnotationInfo}, or else skips over the
-   * annotation.
+   * Extracts an {@link @Retention} or {@link ElementType} {@link ClassFile.AnnotationInfo}, or else
+   * skips over the annotation.
    */
   private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool) {
     int typeIndex = reader.u2();
     String annotationType = constantPool.utf8(typeIndex);
-    boolean retention = annotationType.equals("Ljava/lang/annotation/Retention;");
+    boolean read;
+    switch (annotationType) {
+      case "Ljava/lang/annotation/Retention;":
+      case "Ljava/lang/annotation/Target;":
+        read = true;
+        break;
+      default:
+        read = false;
+        break;
+    }
     int numElementValuePairs = reader.u2();
     ClassFile.AnnotationInfo result = null;
     for (int e = 0; e < numElementValuePairs; e++) {
       int elementNameIndex = reader.u2();
       String key = constantPool.utf8(elementNameIndex);
-      boolean value = retention && key.equals("value");
+      boolean value = read && key.equals("value");
       ElementValue tmp = readElementValue(constantPool, value);
       if (tmp != null) {
         result = new ClassFile.AnnotationInfo(annotationType, true, ImmutableMap.of(key, tmp));
@@ -207,9 +216,13 @@
           int constNameIndex = reader.u2();
           if (value) {
             String typeName = constantPool.utf8(typeNameIndex);
-            if (typeName.equals("Ljava/lang/annotation/RetentionPolicy;")) {
-              String constName = constantPool.utf8(constNameIndex);
-              return new EnumConstValue(typeName, constName);
+            switch (typeName) {
+              case "Ljava/lang/annotation/RetentionPolicy;":
+              case "Ljava/lang/annotation/ElementType;":
+                String constName = constantPool.utf8(constNameIndex);
+                return new EnumConstValue(typeName, constName);
+              default:
+                break;
             }
           }
           break;
@@ -223,8 +236,16 @@
       case '[':
         {
           int numValues = reader.u2();
-          for (int i = 0; i < numValues; i++) {
-            readElementValue(constantPool, false);
+          if (value) {
+            ImmutableList.Builder<ElementValue> elements = ImmutableList.builder();
+            for (int i = 0; i < numValues; i++) {
+              elements.add(readElementValue(constantPool, true));
+            }
+            return new ElementValue.ArrayValue(elements.build());
+          } else {
+            for (int i = 0; i < numValues; i++) {
+              readElementValue(constantPool, false);
+            }
           }
           break;
         }
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 1b148b8..84e0cab 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -399,7 +399,7 @@
    */
   @Nullable
   private Boolean isVisible(ClassSymbol sym) {
-    RetentionPolicy retention = env.get(sym).retention();
+    RetentionPolicy retention = env.get(sym).annotationRetention();
     switch (retention) {
       case CLASS:
         return false;
@@ -597,8 +597,13 @@
         case WILD_TY:
           lowerWildTyTypeAnnotations((WildTy) type, path);
           break;
-        default:
+        case PRIM_TY:
+          lowerTypeAnnotations(((Type.PrimTy) type).annos(), path);
           break;
+        case VOID_TY:
+          break;
+        default:
+          throw new AssertionError(type.tyKind());
       }
     }
 
@@ -636,7 +641,7 @@
         lowerTypeAnnotations(arrayTy.annos(), path);
         path = path.array();
       }
-      lowerTypeAnnotations(base, path.array());
+      lowerTypeAnnotations(base, path);
     }
 
     private void lowerClassTypeTypeAnnotations(ClassTy type, TypePath path) {
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index 6575af3..cdd8605 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -216,9 +216,11 @@
   class PrimTy implements Type {
 
     private final TurbineConstantTypeKind primtkind;
+    private final ImmutableList<AnnoInfo> annos;
 
-    public PrimTy(TurbineConstantTypeKind tykind) {
+    public PrimTy(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos) {
       this.primtkind = tykind;
+      this.annos = annos;
     }
 
     /** The primtive type kind. */
@@ -230,6 +232,11 @@
     public TyKind tyKind() {
       return TyKind.PRIM_TY;
     }
+
+    /** The type annotations. */
+    public ImmutableList<AnnoInfo> annos() {
+      return annos;
+    }
   }
 
   /** A wildcard type, valid only inside (possibly nested) type arguments. */
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index 33fd4bc..ff44533 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -246,6 +246,8 @@
       "type_anno_hello.test",
       "type_anno_array_dims.test",
       "nonconst_unary_expression.test",
+      "type_anno_ambiguous.test",
+      "type_anno_ambiguous_param.test",
     };
     List<Object[]> tests =
         ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java
index af235e7..59698e3 100644
--- a/javatests/com/google/turbine/lower/LowerSignatureTest.java
+++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java
@@ -133,7 +133,9 @@
   public void primitive() {
     assertThat(
             SigWriter.type(
-                new LowerSignature().signature(new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN))))
+                new LowerSignature()
+                    .signature(
+                        new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()))))
         .isEqualTo("Z");
   }
 
@@ -151,7 +153,8 @@
                         new Type.ArrayTy(
                             new ArrayTy(
                                 new ArrayTy(
-                                    new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN),
+                                    new Type.PrimTy(
+                                        TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()),
                                     ImmutableList.of()),
                                 ImmutableList.of()),
                             ImmutableList.of()))))
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 33d378a..c8c37dd 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -95,7 +95,7 @@
             new SourceTypeBoundClass.MethodInfo(
                 new MethodSymbol(new ClassSymbol("test/Test"), "f"),
                 ImmutableMap.of(),
-                new Type.PrimTy(TurbineConstantTypeKind.INT),
+                new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()),
                 ImmutableList.of(),
                 ImmutableList.of(),
                 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PUBLIC,
@@ -129,7 +129,9 @@
                 Type.VOID,
                 ImmutableList.of(
                     new SourceTypeBoundClass.ParamInfo(
-                        new Type.PrimTy(TurbineConstantTypeKind.INT), ImmutableList.of(), false)),
+                        new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()),
+                        ImmutableList.of(),
+                        false)),
                 ImmutableList.of(
                     new Type.TyVar(
                         new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
@@ -172,6 +174,7 @@
             null,
             null,
             null,
+            null,
             ImmutableList.of(),
             null);
 
@@ -192,6 +195,7 @@
             null,
             null,
             null,
+            null,
             ImmutableList.of(),
             null);
 
diff --git a/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous.test b/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous.test
new file mode 100644
index 0000000..483b09b
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous.test
@@ -0,0 +1,45 @@
+%%% Annos.java %%%
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import java.lang.annotation.Target;
+
+@Target(TYPE_USE) @interface A {}
+@Target({TYPE_USE, FIELD}) @interface B {}
+@Target({FIELD}) @interface C {}
+@interface G {}
+
+=== Test.java ===
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import java.lang.annotation.Target;
+
+@Target({TYPE_USE, METHOD}) @interface D {}
+@Target(METHOD) @interface E {}
+@interface F {}
+
+public class Test {
+  @A @B @C @G public int a;
+  @A @B @C @G public String b;
+  @A @B @C @G public int[] c;
+  @A @B @C @G public Inner.InnerMost d;
+
+  public @A @B @C @G int e;
+  public @A @B @C @G String f;
+  public @A @B @C @G int[] g;
+
+  @A @B @C @G Inner.InnerMost h;
+  @A @B @C @G Test.Inner.InnerMost i;
+  @A @B @C @G StaticInner j;
+
+  @A @D @E @F public int f() { return 1; }
+  public @A @D @E @F int g() { return 1; }
+  public @A @D @E @F Inner.InnerMost h() { return null; }
+
+  class Inner {
+    class InnerMost {}
+  }
+
+  static class StaticInner {}
+}
diff --git a/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous_param.test b/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous_param.test
new file mode 100644
index 0000000..b39cc51
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/type_anno_ambiguous_param.test
@@ -0,0 +1,15 @@
+=== Test.java ===
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.ElementType.PARAMETER;
+import java.lang.annotation.Target;
+
+@Target({TYPE_USE, PARAMETER}) @interface A {}
+@Target(TYPE_USE) @interface B {}
+@Target(PARAMETER) @interface C {}
+
+public class Test {
+  void f(@A @B @C int x) {}
+  void g(final @A @B @C int x) {}
+  void h(@A @B @C final int x) {}
+  void i(@A @B @C final int[] x) {}
+}