-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
On partial uninit error point at where we need init #98360
Conversation
r? @oli-obk (rust-highfive has picked a reviewer for you, use r? to override) |
error[E0381]: used binding `z` isn't initialized in all conditions | ||
--> $DIR/chains-without-let.rs:15:36 | ||
| | ||
LL | let z; | ||
| - binding declared here but left uninitialized | ||
LL | if false || { z = 3; false} || z == 3 {} | ||
| ^ use of possibly-uninitialized `z` | ||
| ----- ^ `z` used here but it isn't initialized in all conditions | ||
| | | ||
| binding initialized here in some conditions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oli-obk is this intended or just an unfortunate side-effect of the current machinery? It feels to me that for or binops this code should succeed (while it might not for and binops if the use is outside of the binop chain.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add that big test file you demonstrated as a new UI test.
@@ -1,8 +1,13 @@ | |||
error[E0381]: use of possibly-uninitialized variable: `y` | |||
error[E0381]: used binding `y` isn't initialized in all conditions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too happy with the in all conditions
phrase.
Would be ok I think if the label were kept as ^ use of possibly-uninitialized
and the label on the if condition would say
if this condition is false,
y
is not initialized.
The message about missing an else arm is a proposal to fix, not really an explanation of what's wrong. Cluttering more by adding a further label at the }
of the if
seems not great tho.... idk, what's your opinion on these ramblings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about "binding y
is used when possibly uninitialized" for the message?
I like your proposal for the if condition label, it's more straight forward to understand.
Regarding the "missing else" label, I can reword it to be similar to the other if/else labels, pointing at the }
feels less great when rendered out, but I can try it. Pointing at the whole if could get verbose quickly, but I'm not too worried about that.
LL | let x: isize; | ||
| - binding declared here but left uninitialized | ||
LL | if 1 > 2 { | ||
| ----- `x` is uninitialized if this condition is met |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, I would expect this message (or its inverse) no matter whether there's an else arm or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Because the condition's value can be statically determined?
I think what you're saying is that in the case where
if y {
x = 10;
} else {
println!("whoops");
}
The same message can be shown as for
if y {
x = 10;
}
But that is not the case because for the former we point at the else
, which we don't have, that's why that case is special cased.
LL | v += 1; | ||
| ^^^^^^ use of possibly-uninitialized `v` | ||
| ^^^^^^ `v` used here but it isn't initialized |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably improve the span here at some point
@@ -326,6 +312,100 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | |||
} | |||
} | |||
|
|||
fn report_use_of_uninitialized( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function and its dependencies could live in another module so they're grouped together
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're already in a diagnostics
module, right? I can add a new mod if you want with the whole thing, sure.
let body = map.body(body_id); | ||
|
||
let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; | ||
visitor.visit_body(&body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could generalize the logic by walking the MIR instead. In MIR you can write one dataflow pass that finds all the branches that do not initialize the variable, irrespective of whether the branch came from an if
, a match
or any other control flow.
This also means you would not have to rely on spans, but can use the MIR location directly (potentially helping with desugarings and macros). And since you can map back to a hir id from a location or source info (via https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/struct.SourceScopeData.html#structfield.local_data), you can still generate high quality diagnostics by checking what kind of hir item some MIR thing belongs to.
I'm not sure if that effort is worth it, and we could do it as a separate PR after this one is merged, but it feels a bit cleaner than span fudging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this, I was just held back by my lack of MIR knowledge to change the logic to accomplish this. I would like to leave that as a follow up for someone else though :)
src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr
Outdated
Show resolved
Hide resolved
@bors r+ |
📌 Commit 8495c64 has been approved by |
🌲 The tree is currently closed for pull requests below priority 1000. This pull request will be tested once the tree is reopened. |
@bors r=oli-obk |
📌 Commit 6dd32f2 has been approved by |
🌲 The tree is currently closed for pull requests below priority 1000. This pull request will be tested once the tree is reopened. |
On partial uninit error point at where we need init When a binding is declared without a value, borrowck verifies that all codepaths have *one* assignment to them to initialize them fully. If there are any cases where a condition can be met that leaves the binding uninitialized or we attempt to initialize a field of an uninitialized binding, we emit E0381. We now look at all the statements that initialize the binding, and use them to explore branching code paths that *don't* and point at them. If we find *no* potential places where an assignment to the binding might be missing, we display the spans of all the existing initializers to provide some context. Fix rust-lang#97956.
On partial uninit error point at where we need init When a binding is declared without a value, borrowck verifies that all codepaths have *one* assignment to them to initialize them fully. If there are any cases where a condition can be met that leaves the binding uninitialized or we attempt to initialize a field of an uninitialized binding, we emit E0381. We now look at all the statements that initialize the binding, and use them to explore branching code paths that *don't* and point at them. If we find *no* potential places where an assignment to the binding might be missing, we display the spans of all the existing initializers to provide some context. Fix rust-lang#97956.
Failed in rollup: #98505 (comment) |
This comment has been minimized.
This comment has been minimized.
When a binding is declared without a value, borrowck verifies that all codepaths have *one* assignment to them to initialize them fully. If there are any cases where a condition can be met that leaves the binding uninitialized or we attempt to initialize a field of an unitialized binding, we emit E0381. We now look at all the statements that initialize the binding, and use them to explore branching code paths that *don't* and point at them. If we find *no* potential places where an assignment to the binding might be missing, we display the spans of all the existing initializers to provide some context.
@bors r=oli-obk |
📌 Commit d6c59cf5bf4b59483b892ec830e6a49e7155e73b has been approved by It is now in the queue for this repository. |
@bors r- |
@bors r=oli-obk |
☀️ Test successful - checks-actions |
Finished benchmarking commit (9b21131): comparison url. Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)Results
CyclesResults
If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf. @rustbot label: -perf-regression Footnotes |
When a binding is declared without a value, borrowck verifies that all
codepaths have one assignment to them to initialize them fully. If
there are any cases where a condition can be met that leaves the binding
uninitialized or we attempt to initialize a field of an uninitialized
binding, we emit E0381.
We now look at all the statements that initialize the binding, and use
them to explore branching code paths that don't and point at them. If
we find no potential places where an assignment to the binding might
be missing, we display the spans of all the existing initializers to
provide some context.
Fix #97956.