Handle primitive and void class literals

MOE_MIGRATED_REVID=136373334
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index 11b4b98..76c1c6a 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -40,10 +40,12 @@
 import com.google.turbine.tree.Tree;
 import com.google.turbine.tree.Tree.ArrayInit;
 import com.google.turbine.tree.Tree.Binary;
+import com.google.turbine.tree.Tree.ClassLiteral;
 import com.google.turbine.tree.Tree.ClassTy;
 import com.google.turbine.tree.Tree.Conditional;
 import com.google.turbine.tree.Tree.ConstVarName;
 import com.google.turbine.tree.Tree.Expression;
+import com.google.turbine.tree.Tree.PrimTy;
 import com.google.turbine.tree.Tree.TypeCast;
 import com.google.turbine.tree.Tree.Unary;
 import com.google.turbine.type.Type;
@@ -115,6 +117,8 @@
         throw new AssertionError(t.kind());
       case CONST_VAR_NAME:
         return evalConstVar((ConstVarName) t);
+      case CLASS_LITERAL:
+        return evalClassLiteral((ClassLiteral) t);
       case BINARY:
         return evalBinary((Binary) t);
       case TYPE_CAST:
@@ -132,7 +136,27 @@
     }
   }
 
-  /** Evaluate a reference to another constant variable. */
+  /** Evaluates a class literal. */
+  // TODO(cushon): consider distinguishing between constant field and annotation values,
+  // and only allowing class literals / enum constants in the latter
+  Const evalClassLiteral(ClassLiteral t) {
+    switch (t.type().kind()) {
+      case PRIM_TY:
+        return new Const.ClassValue(new Type.PrimTy(((PrimTy) t.type()).tykind()));
+      case VOID_TY:
+        return new Const.ClassValue(Type.VOID);
+      case CLASS_TY:
+        {
+          ClassTy classTy = (ClassTy) t.type();
+          ClassSymbol classSym = HierarchyBinder.resolveClass(env, owner.scope(), sym, classTy);
+          return new Const.ClassValue(Type.ClassTy.asNonParametricClassTy(classSym));
+        }
+      default:
+        throw new AssertionError(t.type().kind());
+    }
+  }
+
+  /** Evaluates a reference to another constant variable. */
   Const evalConstVar(ConstVarName t) {
     LookupResult result = owner.scope().lookup(new LookupKey(t.name()));
     if (result != null) {
@@ -160,11 +184,6 @@
       sym = Resolve.resolve(env, sym, result.remaining().get(i));
     }
     String name = result.remaining().get(result.remaining().size() - 1);
-    if (name.equals("class")) {
-      // TODO(cushon): consider distinguishing between constant field and annotation values,
-      // and only allowing class literals / enum constants in the latter
-      return new Const.ClassValue(sym.toString());
-    }
     FieldInfo field = inheritedField(env, sym, name);
     if ((field.access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) {
       return new Const.EnumConstantValue(field.sym());
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 00adbb9..ef6cdf7 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -352,7 +352,7 @@
     }
   }
 
-  private static ImmutableMap<String, ElementValue> annotationValues(
+  private ImmutableMap<String, ElementValue> annotationValues(
       ImmutableMap<String, Const> values, Env<ClassSymbol, TypeBoundClass> env) {
     ImmutableMap.Builder<String, ElementValue> result = ImmutableMap.builder();
     for (Map.Entry<String, Const> entry : values.entrySet()) {
@@ -361,12 +361,12 @@
     return result.build();
   }
 
-  private static ElementValue annotationValue(Const value, Env<ClassSymbol, TypeBoundClass> env) {
+  private ElementValue annotationValue(Const value, Env<ClassSymbol, TypeBoundClass> env) {
     switch (value.kind()) {
       case CLASS_LITERAL:
         {
           Const.ClassValue classValue = (Const.ClassValue) value;
-          return new ElementValue.ConstClassValue("L" + classValue.className() + ";");
+          return new ElementValue.ConstClassValue(SigWriter.type(sig.signature(classValue.type())));
         }
       case ENUM_CONSTANT:
         {
diff --git a/java/com/google/turbine/model/Const.java b/java/com/google/turbine/model/Const.java
index e207c79..946b724 100644
--- a/java/com/google/turbine/model/Const.java
+++ b/java/com/google/turbine/model/Const.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.turbine.binder.sym.ClassSymbol;
 import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.type.Type;
 
 /**
  * Compile-time constant expressions, including literals of primitive or String type, class
@@ -626,15 +627,15 @@
   /** A class literal constant. */
   public static class ClassValue extends Const {
 
-    private final String className;
+    private final Type type;
 
-    public ClassValue(String className) {
-      this.className = className;
+    public ClassValue(Type type) {
+      this.type = type;
     }
 
     @Override
     public String toString() {
-      return String.format("%s.class", className);
+      return String.format("%s.class", type);
     }
 
     @Override
@@ -643,8 +644,8 @@
     }
 
     /** The class name. */
-    public String className() {
-      return className;
+    public Type type() {
+      return type;
     }
   }
 
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index 7407e8d..aa08bfc 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -24,6 +24,10 @@
 import com.google.turbine.model.Const;
 import com.google.turbine.model.TurbineConstantTypeKind;
 import com.google.turbine.tree.Tree;
+import com.google.turbine.tree.Tree.ClassLiteral;
+import com.google.turbine.tree.Tree.ClassTy;
+import com.google.turbine.tree.Tree.Expression;
+import com.google.turbine.tree.Tree.VoidTy;
 import com.google.turbine.tree.TurbineOperatorKind;
 import javax.annotation.Nullable;
 
@@ -127,6 +131,24 @@
         return arrayInitializer();
       case IDENT:
         return qualIdent();
+      case BYTE:
+        return primitiveClassLiteral(TurbineConstantTypeKind.BYTE);
+      case CHAR:
+        return primitiveClassLiteral(TurbineConstantTypeKind.CHAR);
+      case DOUBLE:
+        return primitiveClassLiteral(TurbineConstantTypeKind.DOUBLE);
+      case FLOAT:
+        return primitiveClassLiteral(TurbineConstantTypeKind.FLOAT);
+      case INT:
+        return primitiveClassLiteral(TurbineConstantTypeKind.INT);
+      case LONG:
+        return primitiveClassLiteral(TurbineConstantTypeKind.LONG);
+      case SHORT:
+        return primitiveClassLiteral(TurbineConstantTypeKind.SHORT);
+      case BOOLEAN:
+        return primitiveClassLiteral(TurbineConstantTypeKind.BOOLEAN);
+      case VOID:
+        return primitiveClassLiteral(VoidTy.INSTANCE);
       case AT:
         return annotation();
       default:
@@ -134,6 +156,23 @@
     }
   }
 
+  private Expression primitiveClassLiteral(TurbineConstantTypeKind type) {
+    return primitiveClassLiteral(new Tree.PrimTy(type));
+  }
+
+  private Expression primitiveClassLiteral(Tree.Type type) {
+    eat();
+    if (token != Token.DOT) {
+      return null;
+    }
+    eat();
+    if (token != Token.CLASS) {
+      return null;
+    }
+    eat();
+    return new ClassLiteral(type);
+  }
+
   private Tree.Expression maybeCast() {
     eat();
     switch (token) {
@@ -188,12 +227,7 @@
         case TILDE:
         case IDENT:
           {
-            Tree.ClassTy cty = null;
-            for (String bit : cvar.name()) {
-              cty =
-                  new Tree.ClassTy(Optional.fromNullable(cty), bit, ImmutableList.<Tree.Type>of());
-            }
-            return new Tree.TypeCast(cty, primary(false));
+            return new Tree.TypeCast(asClassTy(cvar.name()), primary(false));
           }
         default:
           return expr;
@@ -203,6 +237,14 @@
     }
   }
 
+  private ClassTy asClassTy(ImmutableList<String> names) {
+    ClassTy cty = null;
+    for (String bit : names) {
+      cty = new ClassTy(Optional.fromNullable(cty), bit, ImmutableList.<Tree.Type>of());
+    }
+    return cty;
+  }
+
   private void eat() {
     token = lexer.next();
   }
@@ -373,8 +415,8 @@
           break;
         case CLASS:
           // TODO(cushon): only allow in annotations?
-          bits.add("class");
-          break;
+          eat();
+          return new Tree.ClassLiteral(asClassTy(bits.build()));
         default:
           return null;
       }
diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java
index 8e62718..8fa3f63 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
+import com.google.turbine.tree.Tree.ClassLiteral;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -179,6 +180,13 @@
   }
 
   @Override
+  public Void visitClassLiteral(ClassLiteral classLiteral, Void input) {
+    classLiteral.accept(this, input);
+    append(".class");
+    return null;
+  }
+
+  @Override
   public Void visitAssign(Tree.Assign assign, Void input) {
     append(assign.name()).append(" = ");
     assign.expr().accept(this, null);
diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java
index b4bd669..9ee02b6 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -47,6 +47,7 @@
     UNARY,
     BINARY,
     CONST_VAR_NAME,
+    CLASS_LITERAL,
     ASSIGN,
     CONDITIONAL,
     ARRAY_INIT,
@@ -371,6 +372,30 @@
     }
   }
 
+  /** A JLS 15.8.2 class literal. */
+  public static class ClassLiteral extends Expression {
+
+    private final Type type;
+
+    public ClassLiteral(Type type) {
+      this.type = type;
+    }
+
+    @Override
+    public Kind kind() {
+      return Kind.CLASS_LITERAL;
+    }
+
+    @Override
+    public <I, O> O accept(Visitor<I, O> visitor, I input) {
+      return visitor.visitClassLiteral(this, input);
+    }
+
+    public Type type() {
+      return type;
+    }
+  }
+
   /** A JLS 15.26 assignment expression. */
   public static class Assign extends Expression {
     private final String name;
@@ -861,6 +886,8 @@
 
     O visitConstVarName(ConstVarName constVarName, I input);
 
+    O visitClassLiteral(ClassLiteral classLiteral, I input);
+
     O visitAssign(Assign assign, I input);
 
     O visitConditional(Conditional conditional, I input);
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index c6378b6..4080c1d 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -212,6 +212,7 @@
       "hex_int.test",
       "const_conv.test",
       "bmethod.test",
+      "prim_class.test",
     };
     List<Object[]> tests =
         ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/testdata/prim_class.test b/javatests/com/google/turbine/lower/testdata/prim_class.test
new file mode 100644
index 0000000..c4f51f6
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/prim_class.test
@@ -0,0 +1,25 @@
+=== Anno.java ===
+@interface Anno {
+  Class<?> value() default Anno.class;
+}
+
+=== Annos.java ===
+@interface Annos {
+  Anno[] value() default {};
+}
+
+=== Test.java ===
+class Test {
+  @Annos({
+    @Anno(byte.class),
+    @Anno(char.class),
+    @Anno(double.class),
+    @Anno(float.class),
+    @Anno(int.class),
+    @Anno(long.class),
+    @Anno(short.class),
+    @Anno(boolean.class),
+    @Anno(void.class),
+  })
+  int x;
+}