为什么 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误?
Why does a Java Compiler not produce an unreachable statement error for an unreachable then statement?
如果我尝试编译
for(;;)
{
}
System.out.println("End");
Java 编译器产生一个错误 Unreachable statement
。但是,如果我添加另一个“unreachable”(据我所知)break
语句并使其成为:
for(;;)
{
if(false) break;
}
System.out.println("End");
编译通过。为什么它不产生错误?
行为定义在the JLS description of unreachable statements:
The then-statement is reachable iff the if-then statement is reachable.
因此编译器确定 then 语句 (break;
) 是可达的,而不管 if
.
中的条件如何
更进一步,强调我的:
A basic for
statement can complete normally iff at least one of the following is true:
- The for statement is reachable, there is a condition expression, and the condition expression is not a constant expression (§15.28) with value true.
- There is a reachable
break
statement that exits the for statement.
所以for可以正常完成,因为then语句中包含一个break
。正如您所注意到的,如果将 break
替换为 return
.
,它将不起作用
基本原理在本节末尾进行了解释。实质上,if
有一种特殊的处理方式,允许这样的构造:
if(DEBUG) { ... }
其中 DEBUG 可能是一个编译时间常量。
来自the JLS
An if-then statement can complete normally if at least one of the
following is true:
> The if-then statement is reachable and the condition expression is not
a constant expression whose value is true.
> The then-statement can complete normally.
所以if(false)
是允许的。
This ability to "conditionally compile" has a significant impact on,
and relationship to, binary compatibility. If a set of classes
that use such a "flag" variable are compiled and conditional code is
omitted, it does not suffice later to distribute just a new version of
the class or interface that contains the definition of the flag. A
change to the value of a flag is, therefore, not binary compatible
with pre-existing binaries . (There are other reasons for
such incompatibility as well, such as the use of constants in case
labels in switch statements;)
如 my answer to a similar question 中所述,特定构造 if(compile-time-false)
作为显式后门不受不可访问性规则的约束。在这种情况下,编译器因此将您的 break
视为可访问。
基本上无法访问的代码是通过静态without[=20]分析程序检测到的=] 实际上是 运行 代码。而条件将在 运行时 检查。因此,当进行此分析时,它实际上并没有查看条件,而只是检查 break;
是否可以通过 if
.[=12 访问(可访问) =]
Java 没有检测到 all 不可访问语句的核心原因是通常无法回答代码是否可访问。这是因为 halting problem 在图灵机上是不可判定的。
所以,很明显无法检测到所有无法访问的语句,但为什么不尝试评估条件呢?现在想象一下,使用的条件不仅仅是 false
,而是 ~x == x
。例如,所有这些语句将为每个 int x
(source) 打印 true
。
System.out.println((x + x & 1) == 0);
System.out.println((x + -x & 1) == 0);
System.out.println((-x & 1) == (x & 1));
System.out.println(((-x ^ x) & 1) == 0);
System.out.println((x * 0x80 & 0x56) == 0);
System.out.println((x << 1 ^ 0x1765) != 0);
语句可能相当复杂;解决它们需要时间。它会显着增加构建时间,毕竟它不会检测所有无法访问的语句。编译器旨在付出一些努力,但不会为此花费太多时间。
剩下的唯一问题是:在哪里停止解析条件?其原因似乎没有数学依据,而是基于使用场景。 JLS-14.21
给出了您的特定案例的基本原理
如果我尝试编译
for(;;)
{
}
System.out.println("End");
Java 编译器产生一个错误 Unreachable statement
。但是,如果我添加另一个“unreachable”(据我所知)break
语句并使其成为:
for(;;)
{
if(false) break;
}
System.out.println("End");
编译通过。为什么它不产生错误?
行为定义在the JLS description of unreachable statements:
The then-statement is reachable iff the if-then statement is reachable.
因此编译器确定 then 语句 (break;
) 是可达的,而不管 if
.
更进一步,强调我的:
A basic
for
statement can complete normally iff at least one of the following is true:
- The for statement is reachable, there is a condition expression, and the condition expression is not a constant expression (§15.28) with value true.
- There is a reachable
break
statement that exits the for statement.
所以for可以正常完成,因为then语句中包含一个break
。正如您所注意到的,如果将 break
替换为 return
.
基本原理在本节末尾进行了解释。实质上,if
有一种特殊的处理方式,允许这样的构造:
if(DEBUG) { ... }
其中 DEBUG 可能是一个编译时间常量。
来自the JLS
An if-then statement can complete normally if at least one of the following is true:
> The if-then statement is reachable and the condition expression is not a constant expression whose value is true.
> The then-statement can complete normally.
所以if(false)
是允许的。
This ability to "conditionally compile" has a significant impact on, and relationship to, binary compatibility. If a set of classes that use such a "flag" variable are compiled and conditional code is omitted, it does not suffice later to distribute just a new version of the class or interface that contains the definition of the flag. A change to the value of a flag is, therefore, not binary compatible with pre-existing binaries . (There are other reasons for such incompatibility as well, such as the use of constants in case labels in switch statements;)
如 my answer to a similar question 中所述,特定构造 if(compile-time-false)
作为显式后门不受不可访问性规则的约束。在这种情况下,编译器因此将您的 break
视为可访问。
基本上无法访问的代码是通过静态without[=20]分析程序检测到的=] 实际上是 运行 代码。而条件将在 运行时 检查。因此,当进行此分析时,它实际上并没有查看条件,而只是检查 break;
是否可以通过 if
.[=12 访问(可访问) =]
Java 没有检测到 all 不可访问语句的核心原因是通常无法回答代码是否可访问。这是因为 halting problem 在图灵机上是不可判定的。
所以,很明显无法检测到所有无法访问的语句,但为什么不尝试评估条件呢?现在想象一下,使用的条件不仅仅是 false
,而是 ~x == x
。例如,所有这些语句将为每个 int x
(source) 打印 true
。
System.out.println((x + x & 1) == 0);
System.out.println((x + -x & 1) == 0);
System.out.println((-x & 1) == (x & 1));
System.out.println(((-x ^ x) & 1) == 0);
System.out.println((x * 0x80 & 0x56) == 0);
System.out.println((x << 1 ^ 0x1765) != 0);
语句可能相当复杂;解决它们需要时间。它会显着增加构建时间,毕竟它不会检测所有无法访问的语句。编译器旨在付出一些努力,但不会为此花费太多时间。
剩下的唯一问题是:在哪里停止解析条件?其原因似乎没有数学依据,而是基于使用场景。 JLS-14.21
给出了您的特定案例的基本原理