Check interface and non-interface types in `extends` and `implements` lists

PiperOrigin-RevId: 504078954
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index edfacba..ec579e7 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -206,7 +206,8 @@
         break;
       case CLASS:
         if (base.decl().xtnds().isPresent()) {
-          superClassType = bindClassTy(bindingScope, base.decl().xtnds().get());
+          superClassType =
+              checkClassType(bindingScope, base.decl().xtnds().get(), /* expectInterface= */ false);
         } else if (owner.equals(ClassSymbol.OBJECT)) {
           // java.lang.Object doesn't have a superclass
           superClassType = null;
@@ -228,7 +229,7 @@
     }
 
     for (Tree.ClassTy i : base.decl().impls()) {
-      interfaceTypes.add(bindClassTy(bindingScope, i));
+      interfaceTypes.add(checkClassType(bindingScope, i, /* expectInterface= */ true));
     }
 
     ImmutableList.Builder<ClassSymbol> permits = ImmutableList.builder();
@@ -449,6 +450,30 @@
     throw new AssertionError(a.tyKind());
   }
 
+  private Type checkClassType(CompoundScope scope, ClassTy tree, boolean expectInterface) {
+    Type type = bindClassTy(scope, tree);
+    if (type.tyKind().equals(Type.TyKind.ERROR_TY)) {
+      return type;
+    }
+    HeaderBoundClass info = env.getNonNull(((Type.ClassTy) type).sym());
+    boolean isInterface;
+    switch (info.kind()) {
+      case INTERFACE:
+      case ANNOTATION:
+        isInterface = true;
+        break;
+      default:
+        isInterface = false;
+        break;
+    }
+    if (expectInterface != isInterface) {
+      log.error(
+          tree.position(),
+          expectInterface ? ErrorKind.EXPECTED_INTERFACE : ErrorKind.UNEXPECTED_INTERFACE);
+    }
+    return type;
+  }
+
   /**
    * A generated for synthetic {@link MethodSymbol}s.
    *
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index f839345..8031fa5 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -57,6 +57,8 @@
     BAD_MODULE_INFO("unexpected declaration found in module-info"),
     UNCLOSED_COMMENT("unclosed comment"),
     UNEXPECTED_TYPE("unexpected type %s"),
+    EXPECTED_INTERFACE("expected interface type"),
+    UNEXPECTED_INTERFACE("unexpected interface type"),
     UNEXPECTED_MODIFIER("unexpected modifier: %s"),
     PROC("%s");
 
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index 1a83f0d..a1bea05 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -681,7 +681,9 @@
           "class T extends T {}",
         },
         {
-          "<>:1: error: cycle in class hierarchy: T", "class T extends T {}", "                ^",
+          "<>:1: error: cycle in class hierarchy: T", //
+          "class T extends T {}",
+          "                ^",
         },
       },
       {
@@ -692,6 +694,19 @@
           "<>:1: error: cycle in class hierarchy: T",
           "class T implements T {}",
           "                   ^",
+          "<>:1: error: expected interface type",
+          "class T implements T {}",
+          "                   ^",
+        },
+      },
+      {
+        {
+          "interface T extends T {}",
+        },
+        {
+          "<>:1: error: cycle in class hierarchy: T",
+          "interface T extends T {}",
+          "                    ^",
         },
       },
       {
@@ -768,7 +783,9 @@
           "@interface Test {}",
         },
         {
-          "<>:3: error: missing required annotation argument: value", "@Retention", "^",
+          "<>:3: error: missing required annotation argument: value", //
+          "@Retention",
+          "^",
         },
       },
       {
@@ -958,6 +975,25 @@
       },
       {
         {
+          "class C {}", //
+          "interface I {}",
+          "class A extends I implements C {}",
+          "interface B extends C {}",
+        },
+        {
+          "<>:3: error: unexpected interface type",
+          "class A extends I implements C {}",
+          "                ^",
+          "<>:3: error: expected interface type",
+          "class A extends I implements C {}",
+          "                             ^",
+          "<>:4: error: expected interface type",
+          "interface B extends C {}",
+          "                    ^",
+        },
+      },
+      {
+        {
           "class T<X, X> {", //
           "  <Y, Y> void f() {}",
           "}",
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index 40387ac..52b769b 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -265,7 +265,7 @@
             parseLines(
                 "import java.lang.annotation.Target;",
                 "import java.lang.annotation.ElementType;",
-                "public class C implements B {",
+                "public class C extends B {",
                 "  @Target(ElementType.TYPE_USE)",
                 "  @interface A {};",
                 "}"));
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index fdcfbb3..f04ef27 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -545,7 +545,7 @@
             .put(
                 "Test.java",
                 lines(
-                    "public class Test extends B.BM {", //
+                    "public class Test implements B.BM {", //
                     "  I i;",
                     "}"))
             .build();
diff --git a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
index 11dedbf..bc6d9e6 100644
--- a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
+++ b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
@@ -77,17 +77,17 @@
       },
       {
         "=== I.java ===",
-        "abstract class I {",
-        "  abstract Integer f();",
+        "interface I {",
+        "  default Integer f() {}",
         "}",
         "=== J.java ===",
-        "interface J extends I {",
-        "  default Integer f() {",
+        "class J implements I {",
+        "  Integer f() {",
         "    return 42;",
         "  }",
         "}",
         "=== Test.java ===", //
-        "class Test extends I implements J {",
+        "class Test extends J implements I {",
         "}",
       },
       {