-
Notifications
You must be signed in to change notification settings - Fork 360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initialized state when calling super methods #3760
Comments
Thanks for your issue report. I'm sorry you are having trouble. First exampleIn your code as given, the error message is correct. This declaration: class Sup {
public int compute() { ... }
} states that compute requires a fully-initialized receiver. The message states the mismatch:
No, public class Test {
public static void main(String[] args) {
new Sub(1, 2);
}
}
class Sup {
final int a;
final int b;
public Sup(int a, int b) {
this.a = a;
this.b = b;
}
public int compute() {
System.out.printf("called Sup.compute %s %s%n", a, b);
return this.a + this.b + compute2();
}
public int compute2() {
System.out.printf("called Sup.compute2 %s %s%n", a, b);
return 0;
}
}
class Sub extends Sup {
final int c;
final int d;
public Sub(int a, int b) {
super(a, b);
this.c = super.compute();
this.d = 1;
}
@Override
public int compute() {
System.out.printf("called Sub.compute %s %s %s %s%n", a, b, c, d);
return c;
}
@Override
public int compute2() {
System.out.printf("called Sub.compute2 %s %s %s %s%n", a, b, c, d);
return c / d;
}
} I see that the text in the manual is misleading. (It was trying to be colloquial and imprecise, but it did not convey the key ideas clearly.) I have opened pull request #3810 to improve it and to add an example. Can you take a look to see if that addresses your concern? Please let me know if you can suggest any other improvements to the documentation. Second exampleThe Initialization Checker is focused on ensuring correct assignment of fields in constructors with respect to preventing null pointer exceptions, as opposed to other types of initialization errors. Thus, it neglected to check accesses to fields fields of primitive type outside of constructors. I have updated the manual (in pull request #3810) to clarify the scope of the Initialization Checker, which was not clear. Again, your comments on that are welcome. I agree with your bug report: expanding the scope of the Initialization Checker would be a good enhancement. It would then reject any fields that are currently implicitly initialized to null, 0, or false. You could open a separate issue for that feature request. |
Thanks @mernst for your response. Don't feel sorry, even as-is your tool already helped us a lot :). With your version of the example, I can see that this error message would make sense, because After all, I am just looking for a way to make the checks pass without any errors (and preferably without using Regarding your changes towards the documentation: I think it disarms some of the traps I fell victim to. I think what previously threw me off was the concept of "when treated as Come to think of it, when instantiating But this would mean that you can't really rely on any declaration type of a variable but instead require a full data-flow analysis everytime. I can understand if you default to a pessimistic world assumption ("it could always be a sub type") to keep things more managable for now. (But again: stating this fact somewhere would help understanding the decision process of the checker). Other than that, I only asked myself if |
The analysis doesn't detect this. The most important thing to understand about type-checking is that it is modular. While analyzing one method, the analysis only uses the signatures of other methods; it does not look into the implementation of any other method. So, while analyzing public Sub(int a, int b) {
super(a, b);
this.c = super.compute();
} the type-checker only uses the signature of It would be possible to enrich the Nullness Checker's annotation language, so that the signature of I think you are suggesting a
This would be possible, but it would be necessary to more precisely define it. (I have not thought through the details.) If it is uncommon, you could suppress the warning with an explanation of why the code is correct despite the false positive warning that arises due to the fact that the Nullness Checker's specification language is not exhaustive.
No. I slightly misquoted you, because you actually asked:
The first clause of your sentence is irrelevant. Given a variable, the type-checker knows its type, but the type-checker does not re-analyze all paths through the program to determine richer facts than the type.
Yes, you are correct. Type-checking examines types, not the data flow that produced the type.
That was a typo from editing the code after pasting it in from my IDE. Thanks for catching my goof. |
Ok, with this kind of analysis the error message makes sense then. So this issue is not so much a bug report anymore but a feature request if anything. However, this is not very pressing for us as it only affects a single instance. I was just on a bug-crusade and wondered if there was a way to work-around the report of this seemingly safe case. Enhancing the type-checker with something like a I was also thinking about, wether or not you actually need an annotation. For example, the framework already detects if constructors call (overridable) methods, so the same (or similar) check could be used for this use-case. But with constructors you probably never want to call other methods, whereas delegation is generally fine with normal methods. Also, annotated (parent) methods would make it easier to detect whether or not sub-classes break this contract. Anyway, at this point this is just brainstorming. Feel free to tag this issue as a (low-level) feature request or close it since your changes on the documentation should address the source of confusion for this issue. And thank you for your thorough explanations. |
I'm glad that things are clearer now. Thanks for prompting me to improve the documentation.
In practice, constructors calling methods is not uncommon. If it never happened, there would be less need for the Initialization Checker. I will close this issue for now, but others may find the discussion helpful. |
Version 3.8.0 of the Checker Framework contains an Initialized Fields Checker that warns when a field is not initialized by a constructor. This is more general than the Initialization Checker, which only checks that |
Consider the following code:
When checking this code with the default
NullnessChecker
, I get the following errorAccording to the documentation on partial initialization (esp. case 3)
Sup
's constructor has completed and therefore when treated asSup
(we callSup
's version ofcompute
) its state should be@Initialized
, right?If I refactor
Sup#compute
toI have to refactor the
Sub#compute
method as well. However, here I have to writewhich doesn't seem correct, because I need all fields of
Sub
to be initialized for this method to work properly. However, the check (wrongfully?) passes. Writingwould be correct for the method, but then the checker (rightfully) throws an error due to parameter contra-variance.
Am I missing something or is this a bug in the detection logic? I used version 3.7.0 via Maven. If necessary I can share the project in a repo.
The text was updated successfully, but these errors were encountered: