10

How should I understand this Java compiler behaviour?

while (true) return;
System.out.println("I love Java");
// Err: unreachable statement

if (true) return;
System.out.println("I hate Java");
// OK.

Thanks.

EDIT:

I find out the point after a few minutes:

In the first case compiler throws error because of infinite loop. In both cases compiler does not think about the code inside the statement consequent.

EDIT II:

What impress me on javac now is:

    if (true) return; // Correct
}
    while (true) return; // Correct
}

It looks like javac knows what is inside both loop and if consequent, but when you write another command (as in the first example) you get non-equivalent behaviour (which looks like javac forgot what is inside loop/if).

public static final EDIT III: As the result of this answer I may remark (hopefully correct): Expressions as if (arg) { ...; return;} and while (arg) { ...; return;} are equivalent both semantically and syntactically (in bytecode) for Java iff argv is non-constant (or effectively final type) expression. If argv is constant expression bytecode (and behaviour) may differs.

Disclaimer This question is not on unreachable statements but different handling of logically equivalent expressions such as while true return and if true return.

marek094
  • 424
  • 2
  • 11
  • 7
    It's a case of "If I write crazy code, the compiler may also act crazy". – Kayaman Feb 11 '17 at 13:01
  • 1
    @Andremoniy Nope. Please read carefully. – marek094 Feb 11 '17 at 13:38
  • @marek094, what is your intention for such comparison? Java is not a lower level language and the so to say compiler is not a machine language compiler. What is the exact answer you are looking for, or the exact question, you intend to ask? Also, considering the number of virtual machines and compilers available today, it is so to say very generic and less informant question, in my opinion. – Siddharth Tyagi Feb 11 '17 at 13:42
  • @SiddharthTyagi It is a preparation for my exam, where I am expected answer similar questions written on a paper. ¯\_(ツ)_/¯ – marek094 Feb 11 '17 at 13:51
  • @marek094 sounds like OCJP or OCJD. Try to learn the rules from the jls, you only need 60% to pass (i think). After the exam you will never need it again. – k5_ Feb 11 '17 at 14:02
  • The problem with your question, and the reason why @Kayaman is correct is highlighted in your disclaimer. The two statements are **not** logically equivalent. One is a conditional, and one is a loop. They use the same keywords within their checks, but they have no where close to the same functionality, intent, or ramifications. – krillgar Feb 11 '17 at 14:13
  • And this is also related: [Why is an if/else if/else for a simple boolean not giving an "unreachable code" error](//stackoverflow.com/q/36086502) – Tom Feb 11 '17 at 14:28
  • @krillgar There are two parts of logic - semantic and syntax. These two expressions are semantically equivalent. Syntactically - the resulting byte code equality depends on non-/constant expression in argument (as you can see in k5_ answer). – marek094 Feb 11 '17 at 14:35
  • 1
    @marek094 you only have a reasonable semantic if the syntax is correct. None compilable code might have an intention of semantic, but no actual semantic. – k5_ Feb 11 '17 at 14:43

3 Answers3

14

There are quite strict rules when statements are reachable in java. These rules are design to be easily evaluated and not to be 100% acurate. It should prevent basic programming errors. To reason about reachability in java you are restricted to these rules, "common logic" does not apply.

So here are the rules from the Java Language Specification 14.21. Unreachable Statements

An if-then statement can complete normally iff it is reachable.

So without an else, statements after an if-then are always reachable

A while statement can complete normally iff at least one of the following is true:

  • The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.

  • There is a reachable break statement that exits the while statement.

The condition is a constant expression "true", there is no break. Hence it does not complete normally.

k5_
  • 5,450
  • 2
  • 19
  • 27
  • 2
    I remember seeing rationale for this. Basic idea is that you can have `if(COMPILATION_SWITCH) return; A` and have program compiling properly regardless of value of the switch. – zch Feb 11 '17 at 19:49
5

According to the docs:

Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.

nobody
  • 19,814
  • 17
  • 56
  • 77
squiroid
  • 13,809
  • 6
  • 47
  • 67
2

If you change your code slightly (remove the constant expression), so it doesnt trigger javac reachability it will actually produce identical bytecode for both.

static boolean flag = true;

static void twhile(){
    while (flag) return;
    System.out.println("Java");
}
static void tif(){
    if (flag) return;
    System.out.println("Java");
}

The resulting bytecode:

  static void twhile();
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
      StackMap locals:
      StackMap stack:
     0: getstatic     #10                 // Field flag:Z
     3: ifeq          7
     6: return
      StackMap locals:
      StackMap stack:
     7: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
    10: ldc           #26                 // String Java
    12: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    15: return
      LineNumberTable:
    line 8: 0
    line 9: 7
    line 10: 15
      LocalVariableTable:
    Start  Length  Slot  Name   Signature
      StackMapTable: number_of_entries = 1
    frame_type = 7 /* same */

  static void tif();
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
      StackMap locals:
      StackMap stack:
     0: getstatic     #10                 // Field flag:Z
     3: ifeq          7
     6: return
      StackMap locals:
      StackMap stack:
     7: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
    10: ldc           #26                 // String Java
    12: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    15: return
      LineNumberTable:
    line 12: 0
    line 13: 7
    line 14: 15
      LocalVariableTable:
    Start  Length  Slot  Name   Signature
      StackMapTable: number_of_entries = 1
    frame_type = 7 /* same */
k5_
  • 5,450
  • 2
  • 19
  • 27
  • Yeah, I read byte code for the constant-expression case and its differs which is quite obscure. – marek094 Feb 11 '17 at 14:29
  • @marek094 as you didnt provide a compilable example for the constant-expression case it is hard to compare. In my tests it creates "noops" with different fluff around it. – k5_ Feb 11 '17 at 14:33
  • You are right, I meant the second example from my question. I know, than it is not the same situation but also interesting. – marek094 Feb 11 '17 at 15:02