disable an intellij compiler error - java

I'm getting a "Variable TMP_1 might not have been initialized" error. Here's the snippet:
10 case 1000:
11 Double TMP_1 = len(T$);
12 I = 1d;
13 case 1001:
14 if (I.compareTo(TMP_1) > 0) {
The error is being reported on line 14. In my program it isn't possible to get to case 1001 without executing the code block at case 1000. Apparently Intellij can't figure that out. How can I disable this error? I'd rather take my changes with a null pointer exception.
The source code was generated by a compiler I wrote (the source language is an ancient BASIC.) Relocating the assignment on line 11 would be very difficult.
EDIT - See Mechanical snail's explanation below. This isn't a compiler bug at all; this is a simple program bug. The issue is that the way I have simulated BASIC's GOTO statement requires that I leave the switch statement. And when I do the tmp variable goes out of scope.
Final edit - I changed the code generator to remove the TMP variables entirely.
case 2026:
V = (asc(V$)) - (asc(" "));
dataCursor.restore();
for (J = 1d; J <= ((V * 8d) * 10d); J++) {
X = dataCursor.read();
}
Previously the arithmetic in the for loop was being done using tmp variables set before the 2026 label. Now because there aren't any, there's no problem.

The Java compiler isn't smart enough to prove that the variable you're switching on will never be 1001 until after the code that initializes the variable is executed. Remember that Java variable declarations are completely static; by design, Java only allows your variable to be used in ways that make sense, i.e. are initialized before use. And proving that this happens, for general code, is equivalent to solving the halting problem. (For all that the compiler knows, the expression I.compareTo(TMP_1) > 0 could be nonsense, since it refers to a nonexistent variable. (More precisely, the variable is declared in the scope of the switch statement's body, but the code that initializes it would not execute if you skip to the label case 1001:.))
You aren't permitted to turn this error into a warning; that's one of the drawbacks of a static language. In particular, the Java Language Specification, chapter 16 requires:
For every access of a local variable [...] x, x must be definitely assigned before the access, or a compile-time error occurs.
and the variable is not "definitely assigned" (as defined in the spec) before access. IntelliJ compiles your code using a Java compiler (usually javac). Since what you're trying to do is required to be an error by the standard, what you want is impossible (short of editing the compiler, and then it wouldn't be Java anymore).
Workaround
Instead, simply declare your variable in the surrounding scope, and initialize it to a dummy value. For example:
Double TMP_1 = null;
while(exitVar) {
switch(lblToGoTo) {
...
case 1000:
TMP_1 = len(T$);
I = 1d;
case 1001:
if (I.compareTo(TMP_1) > 0) { ... }
...
}
}

Related

Why don't java & js allow variable declaration in a condition

int a; // why can't I put this in the condition itself
if((a = readData()) > 0){
// do something..
}
I'm wondering why java and javascript don't allow me to declare my variable in the (condition). I understand that it has to do with the fact that a variable declaration has to be the first thing on the line -except for for loops- but what is the underlying reason ? In C++ it seems like they can do it. I don't know C++ so apologizes if I misunderstood.
The reason is simplicity of scope rules. If you allowed variable declaration within a condition, you'd have to define the scope of such a variable, and what it means for a declaration to have a value. Neither is straightforward. Requiring variables in an expression to be already defined is simple, and has no downside. Allowing that is complicated, and would obfuscate the source. That is a posteriori reasoning, of course. It might just be that the language designers had other reasons in the moment.
Why do you care? Bottom line: because the JLS says so.
According to the Java Language Specification (https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.9), an if statement has the following form:
if ( Expression ) Statement [else ...]
A variable assignment, aka an AssignmentExpression, is just one of many sub-types of the abstract Expression (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26), so you can use its result (which happens to be the value that was assigned) in your if statement.
Its simpel:
A If-statement ist defined as:
if (Boolean) {...
We can now substitute:
if (var1 operator var2){... // with "_ operator _" => Boolean
If we now put i=1 as var1 we would get:
if ((i=1) operator var2){... // compile error: i cannot resolved to a local variable
or int i = 1:
if ((int i = 1) operator var2){...
The obvious problem is: int i = 1 is a type declaration and not a variable and we learned above thet the if clause only accept Boolean variables or expression which lead to a Boolean vaiable...!
I wasn't satisfied with the answer here, so like any normal person would do, I learned java byte code. Well, sort of... the rudimentary. So if anyone read this keep in mind that my findings have some extrapolations in them and could be inaccurate.. However it makes sens why it doesn't happen java (much less in javascript where my head was at when asking this) and it is ho so simple.
So now the byte code part:
here is what I got for this:
int a = 9;
System.out.println(a); // just here to prevent some optimization
if((a = 18) > 15){
System.out.println(a);
}
Code:
0: bipush 9
2: istore_1
// -- removed the System.out.print --
10: bipush 18
12: dup
13: istore_1
14: bipush 15
16: if_icmple 26
Here is what happens:
0: push 9 onto the stack
2: store it in the var a
10: push 18 onto stack
12: duplicate 18 on top of the stack
13: store it in var a
14 : push 15
16: the if statement if value1 is less than or equal to value2, branch to instruction at branchoffset.
So since the content of the condition is evaluated before the if statement itself, if we declared int a in the condition, depending on where we define the scope of int a to be, there would be scoping issues.
If it was inside the if block:
If we suppose int a would be scoped into the if block, it must still be evaluated before reaching it and thus being out of scope !
In other words:
while((int a = 9) > 15)
int a = 9 would be evaluated before reaching the while condition, thus being out of the scope we just defined. We wouldn't enter the while block, but the value of a is well defined! Makes sens why it doesn't happen, doesn't it ?
If we go the other way and says that it is just a shortcut for this:
int a;
while((a = 9) > 15)
Then the language wouldn't be consistent and would bring many confusing code! See the for loop, the variable defined in it is scoped in it not outside for example.
It still doesn't make any sens why it's not possible to do in javascript though. However I don't care enough to find out.
I hope I made it clear, at least it makes some sens to me and I learned something. On a side note, I don't think my question really warranted so much down votes, even tho the answer seems obvious to me now. Not every question on stackoverflow has to be a practical one..

Why isn't the ArrayIndexOutOfBoundsException a compile time error?

Can someone explain to me why ArrayIndexOutOfBoundsException is a run-time exception instead of a compile-time error?
In obvious cases when the indexes are negative or greater than the array size, I don't see why it cannot be a compile-time error.
Edited: especially when the size of the array and even the indexing is known at compile time, for example int[] a = new int[10]; a[-1]=5; This should be a compilation error.
The size of the arrays may be defined only at runtime (for instance, the simplest case, if the size of an array depends on the user input).
Therefore it would be impossible to check at compile time for such kind of exceptions, by checking the accesses of an array without actually know its bounds (size).
Because it can't be detected at compile-time all the time.
Entering a[-1] = 5; is something only novices would do (as Richard Tingle said). So it's not worth the effort to update the language standard just for that kind of error. A more interesting case would be a[SOME_CONSTANT] = 5; where SOME_CONSTANT was defined as static final int SOME_CONSTANT = -1; (or some expression involving only constants that computes to -1) in some other class. Even then, however, if the compiler flagged this as an error, it might catch cases where the programmer has put a[SOME_CONSTANT] = 5; in an if statement that has already checked for negative values of the constant. (I'm assuming here that SOME_CONSTANT is a constant whose value could change if the application's requirements change.) So while the language could, in theory, make it illegal to write an array indexing operation that can't possibly succeed, there are good reasons not to.
P.S. This is a real issue. The Ada language does do some compile-time checking for static expressions that can't succeed, but it doesn't check this case, and there has been some discussion in the last few weeks about whether it should, or whether compilers should be allowed (but not required) to reject programs with array indexing that is known to fail.
There is no way to check all indexes at compile time, because they can be variables and its values can change at runtime. If you have array[i] and i is the result of reading a file, you can evaluate i when executing the program. Even if you use a variable, remember that you can reassign your array changing its capacity. Again, this can be checked only ar runtime.
Check this question for more information: Runtime vs Compile time.
As well as agreeing with the fact that array size can't be checked at compile time, I want to add another note on the limit of the size of an array, which is expected to be in the range of primitive int:
// This compiles, because the size evaluates to an integer.
int[] array = new int[Integer.MAX_VALUE + 1];
// This doesn't compile.
int[] array = new int[Long.MAX_VALUE];
And this error is because of length field (int) of arrays which are special Java objects.
When working with pointers it's possible to have negative indexes and not have an error if you have correctly reserved the memory position you will access. Here is an example. When working with low-level programming languages things like this one are very frequently done but they don't have a lot of sense in high-level languages, at least for me.
int arr[10];
int* p = &arr[2];
int x = p[-2]; // valid: accesses arr[0]
if you try to do:
arr[-5] //you will access and invalid block of memory, this why you get the error.
this may result a very helpful and interesting:
http://www-ee.eng.hawaii.edu/~tep/EE160/Book/chap7/subsection2.1.3.2.html

JAva / Android - Adding to long having no effect

I usually use python/php but currently writing an Android app so playing with java at the moment.
I can't figure out why the following piece of my code doesn't work.
It's meant to take a long representing the time the last check occurred (last_check) and add a predefined number of minutes to it (CHECK_INTERVAL) which is currently set to 1 minute.
Log.i(this.toString(), "Last check: " + Long.toString(last_check));
long add_to_check = CHECK_INTERVAL * 60 * 1000;
long next = last_check + add_to_check;
Log.i(this.toString(), "Add to check: " + Long.toString(add_to_check));
Log.i(this.toString(), "Next check: " + Long.toString(next));
scheduleNextRun(next);
My expectation is that the first log will show the last_check time, the second log will show 60000 and the third log will show the sum of those two.
However, I am getting the same value for the first log and the third log - it's like my addition isn't working at all but I can't figure out why.
I thought I might have had issues with log vs int but have tried adding L to one of the variables and it doesn't make a difference.
FYI the second log is showing 60000 as expected.
thanks
Aaron
Is CHECK_INTERVAL 0? You wrote that "the second log is showing 60000 as expected" but perhaps CHECK_INTERVAL was 0 the first time this code ran, then initialized, then 1 on a later iteration when you're looking at that part of the log.
Are you initializing CHECK_INTERVAL to something non-zero but after this code runs? Do you have an initialization bug?
This problem will be easy to solve if you step through the code in the debugger and watch the results. If you're not using a debugger, do yourself a huge favor and get Android Studio (a wonderful tool built on IntelliJ IDEA) or Eclipse + ADT (a good tool).
Java initialization is defined in a way that's predictable, portable, and useful, but it can still be tricky. E.g. given
class Foo {
static final int A2 = A1 * 1000;
static final int A1 = 60;
}
The JVM first initializes all variables to default values, then runs the initialization expressions in order. Since A1 is 0 when A2's initializer runs, A2 will end up 0.
See Java Puzzlers for more subtle cases such as when one class's initialization code refers to a second class, causing the second class's initialization code to run, which then refers to values in the first class which haven't yet been initialized beyond their default values (0 and null). A class runs its initialization code on first demand, but nothing guarantees that it finishes initializing before those values are used.
Another tricky case happens when one class, C1, refers to a static final value from a second class, C2.A, then you edit the initialization code for A without recompiling class C1. Java has precise rules about when to cache such constants in the first class's .class file, but they aren't the ideal rules, and the compiler doesn't notice that it needs to recompile C1 for this!
BTW 1: If CHECK_INTERVAL is an int, the expression CHECK_INTERVAL * 60 * 1000 will compute an int value and wrap around within a 32-bit signed range. Still, 1 * 60 * 1000 will easily fit in an int.
BTW 2: The first arg to Log.i() is a tag. It's OK to pass in this.toString() [or toString() for short] but the idea is to pass in a constant tag like the current class name that you can use for log filtering.
[Added] Quick intro to Eclipse debugging
In the source code editor, double-click in the left margin to set a breakpoint. Then use the menus or toolbar to "debug as" a Java application rather than "run as". Eclipse will go into its "Debug perspective" (arrangement of views).
https://www.google.com/search?q=eclipse+debugger finds nice tutorials with step-by-step pictures (I checked the first 3; IBM's is the most concise and introduces more features) and videos. The Eclipse docs are good but harder to navigate.
It's all slicker in Android Studio.

Error that is neither syntactic nor semantic?

I had this question on a homework assignment (don't worry, already done):
[Using your favorite imperative language, give an example of
each of ...] An error that the compiler can neither catch nor easily generate code to
catch (this should be a violation of the language definition, not just a
program bug)
From "Programming Language Pragmatics" (3rd ed) Michael L. Scott
My answer, call main from main by passing in the same arguments (in C and Java), inspired by this. But I personally felt like that would just be a semantic error.
To me this question's asking how to producing an error that is neither syntactic nor semantic, and frankly, I can't really think of situation where it wouldn't fall in either.
Would it be code that is susceptible to exploitation, like buffer overflows (and maybe other exploitation I've never heard about)? Some sort of pit fall from the structure of the language (IDK, but lazy evaluation/weak type checking)? I'd like a simple example in Java/C++/C, but other examples are welcome.
Undefined behaviour springs to mind. A statement invoking UB is neither syntactically nor semantically incorrect, but rather the result of the code cannot be predicted and is considered erroneous.
An example of this would be (from the Wikipedia page) an attempt to modify a string-constant:
char * str = "Hello world!";
str[0] = 'h'; // undefined-behaviour here
Not all UB-statements are so easily identified though. Consider for example the possibility of signed-integer overflow in this case, if the user enters a number that is too big:
// get number from user
char input[100];
fgets(input, sizeof input, stdin);
int number = strtol(input, NULL, 10);
// print its square: possible integer-overflow if number * number > INT_MAX
printf("%i^2 = %i\n", number, number * number);
Here there may not necessarily be signed-integer overflow. And it is impossible to detect it at compile- or link-time since it involves user-input.
Statements invoking undefined behavior1 are semantically as well as syntactically correct but make programs behave erratically.
a[i++] = i; // Syntax (symbolic representation) and semantic (meaning) both are correct. But invokes UB.
Another example is using a pointer without initializing it.
Logical errors are also neither semantic nor syntactic.
1. Undefined behavior: Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.
Here's an example for C++. Suppose we have a function:
int incsum(int &a, int &b) {
return ++a + ++b;
}
Then the following code has undefined behavior because it modifies an object twice with no intervening sequence point:
int i = 0;
incsum(i, i);
If the call to incsum is in a different TU from the definition of the function, then it's impossible to catch the error at compile time, because neither bit of code is inherently wrong on its own. It could be detected at link time by a sufficiently intelligent linker.
You can generate as many examples as you like of this kind, where code in one TU has behavior that's conditionally undefined for certain input values passed by another TU. I went for one that's slightly obscure, you could just as easily use an invalid pointer dereference or a signed integer arithmetic overflow.
You can argue how easy it is to generate code to catch this -- I wouldn't say it's very easy, but a compiler could notice that ++a + ++b is invalid if a and b alias the same object, and add the equivalent of assert (&a != &b); at that line. So detection code can be generated by local analysis.

Troubleshooting Java code that refuses to cooperate

The string called "code" doesn't seem to read. Why is that and how do I fix it?
My code (the snippet that causes problems):
String code;
for(int z = 0; z<x;z= z+0) // Repeat once for every character in the input string remaining
{
for(int y=0;y<2;y++) //Repeat twice
{
c = (char)(r.nextInt(26) + 'a'); //Generate a random character (lowercase)
ca = Character.toString(c);
temp = code;
code = temp + ca; //Add a random character to the encoded string
}
My error report:
--------------------Configuration: <Default>--------------------
H:\Java\Compiler.java:66: variable code might not have been initialized
temp = code;
^
1 error
Process completed.
(I am using JCreator 5.00, Java 7.)
(Yes, the error report looks stupid, but it Stack Overflow reads it as coding.)
What value would code have if x is zero? The answer is it would have no value at all (not even null). You could just initialize it to an empty string if you like:
String code = "";
Java requires that every variable is initialized before its value is used. In this example, there is a fairly obvious case in which the variable is used before it is assigned. The Java Language Spec (JLS) doesn't allow this. (If it did, the behaviour of programs would be unpredictable, including ... potentially ... JVM crashes.)
In other cases, the compiler complains when in fact the variable in question is always initialized (or so it seems). Rather than "understanding" your code, or trying to derive a logical proof of initialization, the compiler follows a specified procedure for deciding if the variable is definitely assigned. This procedure is conservative in nature, and the answer it gives is either "it is initialized" or "it might not be initialized". Hence the wording of the compilation error message.
Here is an example in which the compiler will complain, even though it is "obvious" that the variable is initialized before use:
boolean panic;
for (int i = 0; i < 10; i += 2) {
if (i % 2 == 1 && panic) { // compilation error here
System.out.println("Panic!!");
}
}
The definite assignment rules (specified in the JLS) say that panic is NOT definitely initialized at the point indicated. It is a simple matter for a person who understands the basics of formal methods to prove that i % 2 == 1 will always be false. However, the compiler can't. (And even if it could, the code is still in error given JLS rules.)
You've created a reference, but you've never initialized it. Initialize code by changing the first line to
String code = ""
Edit: Zavior pointed out that you can pull an initialized string from the cache rather than allocate space for a new one.
But why are you assigning temp to code and then code to temp plus something else? It can be set to code = code + ca.

Categories