Простая вещь:
S s = new S();
T t = (T)s;
Учитывая текущий код , который вы показываете, компилятор может знать, что это приведение не имеет смысла, и должен завершиться с ошибкой во время выполнения.
Но дело в том: ваш случай здесь является довольно конкретным примером. Обычный вариант использования менее «понятен». Как показал Эран, довольно просто построить подобный пример, где приведение во время выполнения может работать или нет, в зависимости от очень тонких различий.
Таким образом, прагматический ответ таков: тот факт, что компилятор может узнать, что программа недействительна и в дальнейшем завершится с ошибкой, не обязательно приводит к сбою компилятора.
Другими словами: когда вы разрабатываете языки и конструируете компиляторы, всегда есть компромиссы. Как в: иногда просто не стоит добавлять очень специфическую проверку во время компиляции. Вы скорее соглашаетесь с тем, что более общее правило может привести к сбоям во время выполнения, а не во время компиляции.