-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Fix accidental type inference in array coercion #140283
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
base: main
Are you sure you want to change the base?
Conversation
|
rustbot has assigned @petrochenkov. Use |
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.
Please add a test to demonstrate the effect of this change. Especially since with this change, we would be accepting more code than on master (this is the snippet in the original issue):
fn foo() {}
fn bar() {}
fn main() {
let _a = if true { foo } else { bar };
let _b = vec![foo, bar];
let _c = [foo, bar];
d(if true { foo } else { bar });
e(vec![foo, bar]);
f([foo, bar]); // <- this PR now accepts this
}
fn d<T>(_: T) {}
fn e<T>(_: Vec<T>) {}
fn f<T>(_: [T; 2]) {}whereas on master this snippet does not compile w/
error[E0308]: mismatched types
--> src/main.rs:10:7
|
10 | f([foo, bar]);
| - ^^^^^^^^^^ expected `[fn() {foo}; 2]`, found `[fn(); 2]`
| |
| arguments to this function are incorrect
|
= note: expected array `[fn() {foo}; 2]`
found array `[fn(); 2]`
note: function defined here
--> src/main.rs:15:4
|
15 | fn f<T>(_: [T; 2]) {}
| ^ ---------
For more information about this error, try `rustc --explain E0308`.
I'm surprised there are no ui test diffs.
|
r? types |
|
There're some similar errors, but I'm unsure whether it's okay to allow these code. The Rust Reference. fn foo() {}
fn bar() {}
fn main() {
let block_var = 'a: { // Easy to fix, but not specified by the Rust Reference.
if false {
break 'a foo;
}
break 'a bar;
};
let loop_var = loop { // Easy to fix, but not specified by the Rust Reference.
if false {
break foo;
}
break bar;
};
let closure_var = || { // More complicated. But this should work according to the Rust Reference.
if false {
return foo;
}
return bar;
};
} |
Yea I think these all should work, too. Please fix the easy ones and add tests for all of them if we don't already have any |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
fdbaf03 to
b31fa29
Compare
|
Turns out the ‘easy’ cases aren’t so easy after all. I can't fix the inference regressions for now, but I might revisit them later once I understand type inference better. |
|
@rustbot ready |
|
I am not going to get to this in the next 10 days. I'll need to review the general state of inference here and write a T-types FCP text |
|
☔ The latest upstream changes (presumably #141716) made this pull request unmergeable. Please resolve the merge conflicts. |
|
Array expressions normally lub their element expressions' types to ensure that things like This PR changes that to instead fall back to "not knowing" that the argument type is array of infer var, but just having an infer var for the entire argument. Thus we typeck the array expression normally, lubbing the element expressions, and then in the end comparing the array expression's type with the array of infer var type. Things like fn foo() {}
fn bar() {}
fn f<T>(_: [T; 2]) {}
f([foo, bar]);and struct Foo;
struct Bar;
trait Trait {}
impl Trait for Foo {}
impl Trait for Bar {}
fn f<T>(_: [T; 2]) {}
f([&Foo, &Bar as &dyn Trait]);@rfcbot merge |
|
Team member @oli-obk has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Probably this one. I have too many local branches on this issue and the differences are subtle so I can't be sure. This test fails with current compiler as well. |
|
|
🎉 Experiment
Footnotes
|
|
🔔 This is now entering its final comment period, as per the review above. 🔔 |
|
Can you add a comment to My understanding is: when coercing, each branch should use the following expectations for type inference:
Ideally we'd have some sort of What we instead do is use use the expected type of the match as the initial coercion lub and as the expected type for each branch. This allows us to use the lub of "expected type of match" with "types from previous branches" as the expectation. Now, this is obviously wrong if that lub is different from the expected type of the match. However, in that case coercing the final type of the A bigger issue is that for some branches with type Ideally we'd compute the expected type without unnecessarily constraining the expected type of the match when computing the expected type of its branches. |
|
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
|
If |
This comment has been minimized.
This comment has been minimized.
fn foo() {}
fn bar() {}
fn f<T>(_: T]) {}
fn main() {
f(loop {
if true {
break foo;
} else {
break bar;
}
});
}this seems to demonstrate the same behaviour with |
|
Yeah, this is an inherent problem of |
|
This seems to be on the critical path to make #148190 work without even more terrible hacks. So I was wondering, what's the status here and what are the next steps to make progress? :) |
|
@adwinwhite , please, do some minimal change, which fixes bugs, which block @RalfJung . This is needed to finally get rid of ShadowInitBox (we all want this). We need this to work: fn id<T>(x: Box<T>) -> Box<T> { x }
fn main() {
<[_]>::into_vec(id::<[_; _]>(Box::new([&String::new(), "abc"])));
}And this: fn main() {
let functions: [_; _] = [
|x: i32| -> i32 { x + 3 },
|x: i32| -> i32 { x + 3 },
];
let string = String::new();
let a: [_; _] = [&string, "abc"];
let b: [_; _] = ["abc", &string];
}Everything else can be fixed later. Please mention me when answering, I'm not subscribed |
|
@RalfJung @safinaskar I think I have answered all questions from the reviewers but not sure whether all concerns are resolved. I'd appreciate the reviewers' judgment on whether this is ready or needs changes. |
|
@safinaskar please slow down a little. "We need this to work" is building up pressure that we have no right to put on anyone here. @adwinwhite thanks! Oli had to reduce his reviewing so let's assign someone else who was already involved in the discussions above: |
Fixes #136420.
If the expectation of array element is a type variable, we should avoid resolving it to the first element's type and wait until LUB coercion is completed.
We create a free type variable instead which is only used in this
CoerceMany.check_expr_matchandcheck_expr_ifwhereCoerceManyis also used do the same.FCP Proposal:
Remaining inconsistency with
ifandmatch(#145048):The typeck of array always uses the element coercion target type as the expectation of element exprs while
ifandmatchuseNoExpectationif the expected type is an infer var.This causes that array doesn't support nested coercion.
But we can't simply change this behavior to be the same as
ifandmatchsince many code depends on using the first element's type as expectation.