Handle array class literals, which can appear in annotations

MOE_MIGRATED_REVID=137779342
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index 07c95c4..29a8836 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -147,16 +147,22 @@
 
   /** Evaluates a class literal. */
   Const evalClassLiteral(ClassLiteral t) {
-    switch (t.type().kind()) {
+    return new ClassValue(evalClassLiteralType(t.type()));
+  }
+
+  private Type evalClassLiteralType(Tree.Type type) {
+    switch (type.kind()) {
       case PRIM_TY:
-        return new ClassValue(new Type.PrimTy(((PrimTy) t.type()).tykind(), ImmutableList.of()));
+        return new Type.PrimTy(((PrimTy) type).tykind(), ImmutableList.of());
       case VOID_TY:
-        return new ClassValue(Type.VOID);
+        return Type.VOID;
       case CLASS_TY:
-        return new ClassValue(
-            Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) t.type())));
+        return Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) type));
+      case ARR_TY:
+        return new Type.ArrayTy(
+            evalClassLiteralType(((Tree.ArrTy) type).elem()), ImmutableList.of());
       default:
-        throw new AssertionError(t.type().kind());
+        throw new AssertionError(type.kind());
     }
   }
 
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index 87a4e96..c8bca7b 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -152,7 +152,8 @@
       case BOOLEAN:
         return primitiveClassLiteral(TurbineConstantTypeKind.BOOLEAN);
       case VOID:
-        return primitiveClassLiteral(new Tree.VoidTy(position, ImmutableList.of()));
+        eat();
+        return finishClassLiteral(position, new Tree.VoidTy(position, ImmutableList.of()));
       case AT:
         return annotation();
       default:
@@ -161,20 +162,8 @@
   }
 
   private Expression primitiveClassLiteral(TurbineConstantTypeKind type) {
-    return primitiveClassLiteral(new Tree.PrimTy(position, ImmutableList.of(), 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(position, type);
+    return finishClassLiteral(position, new Tree.PrimTy(position, ImmutableList.of(), type));
   }
 
   private Tree.Expression maybeCast() {
@@ -428,6 +417,9 @@
     ImmutableList.Builder<String> bits = ImmutableList.builder();
     bits.add(lexer.stringValue());
     eat();
+    if (token == Token.LBRACK) {
+      return finishClassLiteral(pos, asClassTy(bits.build()));
+    }
     while (token == Token.DOT) {
       eat();
       switch (token) {
@@ -446,6 +438,26 @@
     return new Tree.ConstVarName(pos, bits.build());
   }
 
+  private Expression finishClassLiteral(int pos, Tree.Type type) {
+    while (token == Token.LBRACK) {
+      eat();
+      if (token != Token.RBRACK) {
+        return null;
+      }
+      eat();
+      type = new Tree.ArrTy(position, ImmutableList.of(), type);
+    }
+    if (token != Token.DOT) {
+      return null;
+    }
+    eat();
+    if (token != Token.CLASS) {
+      return null;
+    }
+    eat();
+    return new ClassLiteral(pos, type);
+  }
+
   public Tree.Expression expression() {
     Tree.Expression result = expression(null);
     switch (token) {
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index 5740701..1762282 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -1126,7 +1126,11 @@
       eat(LPAREN);
       while (token != RPAREN) {
         ConstExpressionParser cparser = new ConstExpressionParser(lexer, token);
-        args.add(cparser.expression());
+        Expression arg = cparser.expression();
+        if (arg == null) {
+          throw error("invalid annotation argument");
+        }
+        args.add(arg);
         token = cparser.token;
         if (!maybe(COMMA)) {
           break;
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index 26282e4..cd41dd6 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -254,6 +254,7 @@
       "receiver_param.test",
       "static_member_type_import.test",
       "type_anno_qual.test",
+      "array_class_literal.test",
     };
     List<Object[]> tests =
         ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/testdata/array_class_literal.test b/javatests/com/google/turbine/lower/testdata/array_class_literal.test
new file mode 100644
index 0000000..9033b04
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/array_class_literal.test
@@ -0,0 +1,11 @@
+=== Test.java ===
+@interface Anno {
+  Class<?> value() default Object.class;
+}
+
+class Test {
+  @Anno(byte[].class) int a;
+  @Anno(byte[][].class) int b;
+  @Anno(int[][].class) int c;
+  @Anno(Object[].class) int d;
+}