You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Regression in Error conversion from Infallible #66757
error[E0277]: `?` couldn't convert the error to `E`
--> src/lib.rs:13:24
|
13 | u32::try_from(1u32)?;
| ^ the trait `std::convert::From<()>` is not implemented for `E`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<E as std::convert::From<!>>
= note: required by `std::convert::From::from`
The same result also occurs when the desired error type is transitively From<!>, which seems to come up pretty commonly in the wild since std errors like TryFromIntError are From<!>:
use std::convert::{TryFrom};structE;implFrom<!>forE{fnfrom(_: !) -> E{E}}structF;implFrom<E>forF{fnfrom(_:E) -> F{F}}fnfoo() -> Result<(),F>{
u32::try_from(1u32)?;Ok(())}
error[E0277]: `?` couldn't convert the error to `F`
--> src/lib.rs:20:24
|
20 | u32::try_from(1u32)?;
| ^ the trait `std::convert::From<()>` is not implemented for `F`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<F as std::convert::From<E>>
= note: required by `std::convert::From::from`
@acfoltzer Your example legitimately does not compile, although the error message is wrong in Nightly because of this bug. From conversions have never worked transitively. The impl From<!> for E does not affect your example, you get the same results without it. The ! here comes from <u32 as TryFrom<u32>>::Error
@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 impl From<E> for F rather than impl From<!> for E. But it looks like that happens regardless of the From<!>:
use std::convert::TryFrom;structE;structF;implFrom<E>forF{fnfrom(_:E) -> F{F}}fnfoo() -> Result<(),F>{
u32::try_from(1u32)?;Ok(())}
error[E0277]: `?` couldn't convert the error to `F`
--> src/lib.rs:14:24
|
14 | u32::try_from(1u32)?;
| ^ the trait `std::convert::From<()>` is not implemented for `F`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<F as std::convert::From<E>>
= note: required by `std::convert::From::from`
vs the following, which compiles fine:
use std::convert::TryFrom;structF;fnfoo() -> Result<(),F>{
u32::try_from(1u32)?;Ok(())}
I think what happens in the code that compiles is that the only From<_> trait implemented by E is From<E>, the identity conversion. So the call is <E as From<E>>::from which expects an argument of type E. It works out because ! auto-coerces to E (or anything).
If we add impl From<!> for E (back), now E as From<_> becomes ambiguous. We have a type variable, not fully resolved in type inference, which is then unified with the argument expression passed to from(). That argument has type !, so the type variable is unified with the type !. This unification apparently happens before any coercion has a chance to happen.
Presumably, the compiler then ends up here somehow:
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`
@SimonSapin in your example, the problem has to do with when fallback occurs. When you see x.bar, fallback has not yet occurred, and so the type is not (yet) known. Field access (and the . operator in general) requires that the type be known, so you get an error.
However, code like this does compile, and it shows that x is getting a type of either u32 or () -- we know it's not u32, so it must be (). Interestingly, if you enable fallback-to-!, the code no longer builds. (playground):
#![allow(unreachable_code)]// Uncomment and you will get an error:// #![feature(never_type_fallback)]fnfoo(never: !){let x:_ = never;bar(x);}fnbar<T:Bar>(t:T){}traitBar{}implBarforu32{}implBarfor(){}fnmain(){}
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 ! than to introduce the "split fallback" that I proposed in my previous comments. @llogiq you had previously expressed an interest, not sure if others are interested, but we may want to up the priority on that work (I can try to dedicate a bit more time to getting it started, too).
@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.
Activity
alexcrichton commentedon Nov 25, 2019
Bisection reveals #66607 as the cause, likely #65355
SimonSapin commentedon Nov 25, 2019
Reduced:
(Playground)
Centril commentedon Nov 25, 2019
(Adding
#![feature(never_type_fallback)]resolves the issue.)SimonSapin commentedon Nov 25, 2019
The above went through this doc-comment of the desugaring of
foo?, which wasn’t easy to find:rust/src/librustc/hir/lowering/expr.rs
Lines 1199 to 1209 in 91642e3
SimonSapin commentedon Nov 25, 2019
That explains where the
()comes from, thanks. But it’s not clear to me why fallback should happen in the first place in this code.Centril commentedon Nov 25, 2019
Unclear to me also; cc @nikomatsakis and @arielb1 -- also cc #65992
acfoltzer commentedon Nov 26, 2019
The same result also occurs when the desired error type is transitively
From<!>, which seems to come up pretty commonly in the wild since std errors likeTryFromIntErrorareFrom<!>:SimonSapin commentedon Nov 26, 2019
@acfoltzer Your example legitimately does not compile, although the error message is wrong in Nightly because of this bug.
Fromconversions have never worked transitively. Theimpl From<!> for Edoes not affect your example, you get the same results without it. The!here comes from<u32 as TryFrom<u32>>::Erroracfoltzer commentedon Nov 26, 2019
@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
impl From<E> for Frather thanimpl From<!> for E. But it looks like that happens regardless of theFrom<!>:vs the following, which compiles fine:
Maybe this is a distinct issue?
SimonSapin commentedon Nov 26, 2019
Oooh that’s interesting! Adapting this to the reduced failure from #66757 (comment): removing the
impl From<!> for Emakes that code compile:(Playground)
I think what happens in the code that compiles is that the only
From<_>trait implemented byEisFrom<E>, the identity conversion. So the call is<E as From<E>>::fromwhich expects an argument of typeE. It works out because!auto-coerces toE(or anything).If we add
impl From<!> for E(back), nowE as From<_>becomes ambiguous. We have a type variable, not fully resolved in type inference, which is then unified with the argument expression passed tofrom(). That argument has type!, so the type variable is unified with the type!. This unification apparently happens before any coercion has a chance to happen.Presumably, the compiler then ends up here somehow:
rust/src/librustc/ty/context.rs
Lines 2442 to 2449 in 797fd92
dtolnay commentedon Nov 27, 2019
heim-rs/heim#182 is hitting a variation of this which minimizes to:
Fine on stable/beta, fails on nightly with:
17 remaining items
nikomatsakis commentedon Dec 2, 2019
I agree with @dtolnay as well -- I think a revert is probably the most prudent step right now.
nikomatsakis commentedon Dec 2, 2019
@SimonSapin in your example, the problem has to do with when fallback occurs. When you see
x.bar, fallback has not yet occurred, and so the type is not (yet) known. Field access (and the.operator in general) requires that the type be known, so you get an error.However, code like this does compile, and it shows that
xis getting a type of eitheru32or()-- we know it's notu32, so it must be(). Interestingly, if you enable fallback-to-!, the code no longer builds. (playground):Trait resolution can wait until the end, after fallback has occurred.
nikomatsakis commentedon Dec 5, 2019
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
!than to introduce the "split fallback" that I proposed in my previous comments. @llogiq you had previously expressed an interest, not sure if others are interested, but we may want to up the priority on that work (I can try to dedicate a bit more time to getting it started, too).llogiq commentedon Dec 6, 2019
@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.
nikomatsakis commentedon Dec 6, 2019
@llogiq Not a problem at all! Happy to discuss further the fallback question on Zulip.
Add regression test for rust-lang#66757
pnkfelix commentedon Dec 12, 2019
visiting for T-compiler triage. I think we can remove the nomination label; the immediate fire is being put out via PR #67224
Rollup merge of rust-lang#67224 - nikomatsakis:revert-stabilization-o…
Add regression test for rust-lang#66757
Auto merge of #67224 - nikomatsakis:revert-stabilization-of-never-typ…
Add regression test for rust-lang#66757