From JLS 5.5.1, Reference Type Casting:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1
Given a compile-time reference type S (source) and a compile-time reference type T (target), a casting conversion exists from S to T if no compile-time errors occur due to the following rules.
In our case, S = D (interface), T = C (class).
If S is a class type:
S is an interface, so we skip this.
If S is an interface type:
D is an interface, so we use this branch.
If T is an array type, then S must be the type java.io.Serializable or Cloneable (the only interfaces implemented by arrays), or a compile-time error occurs.
T is not an array type, we skip this.
If T is a class or interface type that is not final (§8.1.1), then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.
We do not have this. We can certainly create one to cause this to happen, but that requires a redefinition of C to fit this model.
Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might).
In other words, the compiler has no reason to throw an error, because it is distinctly possible that elsewhere there exists a possible class that can be used to qualify the cast.
For example, you could have this elsewhere:
class E extends C implements D { ... }
Finalizing C causes a compile-time error:
final class C { ... }
Basically, there exists a possibility that there is an implementation of C that can exist that works at runtime which cannot be verified at compile time.