-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Regression in Error conversion from Infallible #66757
Comments
Reduced: struct E;
impl From<!> for E {
fn from(_: !) -> E {
E
}
}
#[allow(unreachable_code)]
fn foo(never: !) {
<E as From<!>>::from(never); // Ok
<E as From<_>>::from(never); // Inference fails here
} |
(Adding |
The above went through this doc-comment of the desugaring of rust/src/librustc/hir/lowering/expr.rs Lines 1199 to 1209 in 91642e3
|
That explains where the |
Unclear to me also; cc @nikomatsakis and @arielb1 -- also cc #65992 |
The same result also occurs when the desired error type is transitively use std::convert::{TryFrom};
struct E;
impl From<!> for E {
fn from(_: !) -> E {
E
}
}
struct F;
impl From<E> for F {
fn from(_: E) -> F {
F
}
}
fn foo() -> Result<(), F> {
u32::try_from(1u32)?;
Ok(())
}
|
@acfoltzer Your example legitimately does not compile, although the error message is wrong in Nightly because of this bug. |
@SimonSapin Hmm, that wasn't the change I was expecting would make the example compile, but the fact that it doesn't change the behavior is a surprise. What I thought was the telling change is whether it compiles after removing the use std::convert::TryFrom;
struct E;
struct F;
impl From<E> for F {
fn from(_: E) -> F {
F
}
}
fn foo() -> Result<(), F> {
u32::try_from(1u32)?;
Ok(())
}
vs the following, which compiles fine: use std::convert::TryFrom;
struct F;
fn foo() -> Result<(), F> {
u32::try_from(1u32)?;
Ok(())
} Maybe this is a distinct issue? |
Oooh that’s interesting! Adapting this to the reduced failure from #66757 (comment): removing the pub struct E;
#[allow(unreachable_code)]
pub fn foo(never: !) {
<E as From<_>>::from(never);
} I think what happens in the code that compiles is that the only If we add Presumably, the compiler then ends up here somehow: rust/src/librustc/ty/context.rs Lines 2442 to 2449 in 797fd92
|
heim-rs/heim#182 is hitting a variation of this which minimizes to: fn from(e: std::convert::Infallible) -> Box<dyn std::error::Error> {
Box::new(e)
} Fine on stable/beta, fails on nightly with: error[E0277]: the trait bound `(): std::error::Error` is not satisfied
--> src/main.rs:2:5
|
2 | Box::new(e)
| ^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
|
= note: required for the cast to the object type `dyn std::error::Error` |
So ideally we’d like the type variable struct E;
impl From<!> for E {
fn from(x: !) -> E { x }
}
fn foo(never: !) {
<E as From<_>>::from(never);
} And: fn foo(e: !) -> Box<dyn std::error::Error> {
Box::<_>::new(e)
} But not in the case of older https://crates.io/crates/objc versions where that causes UB: fn unconstrained_return<T>() -> Result<T, String> {
let ffi: fn() -> T = transmute(some_pointer);
Ok(ffi())
}
fn foo() {
match unconstrained_return::<_>() {
Ok(x) => x, // `x` has type `_`, which is unconstrained
Err(s) => panic!(s), // … except for unifying with the type of `panic!()`
// so that both `match` arms have the same type.
// Therefore `_` resolves to `!` and we "return" an `Ok(!)` value.
};
} But in a world where I’m afraid that the answer is no, there’s no real difference. Which would mean that we can’t have all three of:
… at the same time. And as we know, if |
triage: P-high. Leaving nomination tag on this, since it seems like we'll need to discuss what action to take on it either this week or next week. |
also, tagging this as T-lang: my interpretation of the analysis above is that where it says "... [we cannot have all three of ] 1. Make |
No, I changing 089229a#diff-da707dc4d90c85e7074dbdecebfe8785R643 I don’t think we should make all empty enums aliases with each other. Rust chose nominal typing over structural typing a long time ago. The name |
Why should |
@est31 |
That's what happens when things are rushed before they are done. We'll either have to 1) wait with things like |
“Rushed” is an interesting choice of word for a feature that was stabilized almost three years after it was formally proposed. |
If it had already waited three years, a few more releases would not have made a large difference, relatively. |
Oh, whoops, I totally misinterpreted the situation about what is going on with So ... its possible this is not a T-lang issue then? Is the T-libs team considering "just" reverting the definition of |
T-libs has not collectively discussed this issue yet. Every comment on mine in this thread so far (including this one) is from me individually. I think we (the Rust project) have a number of possible ways to proceed here, and some of them do involve T-lang. As far as I can tell this is all options including bad ones:
That’s it. I can’t think of anything else. |
A few quick thoughts: @SimonSapin asked in this comment whether there was a difference between the fallback used in a case like I've not gone digging into the code but I believe that at first glance it should be possible to separate the two, so that This would basically mean that the type of an abrupt expression like |
From #66757 (comment) I agree that 1 (shipping as is) is not acceptable. With holidays and ~2 weeks remaining in 1.41 we likely wouldn't have time for 2 (mitigations for the objc style inference change). If Niko's suggestion of differentiating between references to an existing value of type |
@dtolnay That plan sounds good to me.
I agree! Though right now that seems to cause an inference error: #[allow(unreachable_code)]
pub fn foo(never: !) {
let x: _ = never;
// Access non-existent field to make the compiler print
// an error message with the type of `x`
x.bar;
} (Playground) Errors: Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed
--> src/lib.rs:6:5
|
3 | let x: _ = never;
| - consider giving `x` a type
...
6 | x.bar;
| ^ cannot infer type
|
= note: type must be known at this point |
I agree with @dtolnay as well -- I think a revert is probably the most prudent step right now. |
@SimonSapin in your example, the problem has to do with when fallback occurs. When you see However, code like this does compile, and it shows that #![allow(unreachable_code)]
// Uncomment and you will get an error:
// #![feature(never_type_fallback)]
fn foo(never: !) {
let x: _ = never;
bar(x);
}
fn bar<T: Bar>(t: T) { }
trait Bar { }
impl Bar for u32 { }
impl Bar for () { }
fn main() { } Trait resolution can wait until the end, after fallback has occurred. |
We discussed in today's lang-team meeting and decided that the most prudent step is to revert. @Centril asked if someone else could prepare the PR -- any volunteers? We would like after that to pursue the mitigations a bit more actively, since it seems better to have a uniform fallback to |
@nikomatsakis Sorry, I was swamped with other work. Also some pointers to the infer functions I need to use – how do I find out if a type comes from fallback? Or how do I set the fallback? – would be really helpful. |
@llogiq Not a problem at all! Happy to discuss further the fallback question on Zulip. |
visiting for T-compiler triage. I think we can remove the nomination label; the immediate fire is being put out via PR #67224 |
…f-never-type, r=centril Revert stabilization of never type Fixes rust-lang#66757 I decided to keep the separate `never-type-fallback` feature gate, but tried to otherwise revert rust-lang#65355. Seemed pretty clean. ( cc @Centril, author of rust-lang#65355, you may want to check this over briefly )
The following code:
compiles on stable but fails to compile on nightly with:
This was discovered through bytecodealliance/wasmtime#633
The text was updated successfully, but these errors were encountered: