Per https://github.com/rust-lang/rust/issues/49593, we need to rollback the stabilization of ! (and a few dependent things) from 1.26. That's sad, but hopefully only temporary -- we'll bring it back once we can fix the coercion, but we don't want to be tinkering with the core type system in a beta backport. I'm opening this P-high issue specifically to track the backport, not the response.
triage: P-high
cc @canndrew -- this is sorta sad :(
Since ! is hard to search for I just left a comment on probably not the best issue. However I came across a case which now fails to compile I want to ensure does not fall under the radar: https://github.com/rust-lang/rust/issues/48950#issuecomment-380255783
@mitsuhiko
Interesting.
So @alexcrichton and I were saying that it would make sense to revert the TryFrom stabilization PRs first, and then the ! changes. I think he said @SimonSapin was going to do that?
FWIW I think it sucks if we can鈥檛 find a satisfying solution to https://github.com/rust-lang/rust/issues/49593 in time. If we must revert it should鈥檛 be in the opposite order but there鈥檚 no need to separate them either. I could prepare a commit that changes some #[stable] attributes back to #[unstable] and it can land together with the rest.
Argh. I meant to do this today, but I failed to get it done. It's going to be a bit tricky to revert !. If you look at the actual PR (rust-lang/rust#47630), you'll see there was a lot of complicated stuff.
I do not want to just revert back to what we had -- in particular I do not want to keep that flag we had on () type (we used to have two flavors of tuples, bad). However, that was the future compat warning mechanism we were using. We could use a coarser mechanism, where we check for any trait impls that concern () -- or just omit warnings for now. I'm inclined to do the latter at least to start (which in fact sidesteps the majority of the diff from rust-lang/rust#47630).
Hmm, ping @pnkfelix -- do you think you could take on reverting this next week?
FWIW I think it sucks if we can鈥檛 find a satisfying solution to #49593 in time
@SimonSapin I agree, but I feel reluctant to make changes to inference in a beta backport. But maybe it is worth expending some effort to see if we can find a minimal delta that we'd feel comfortable with.
I feel reluctant to make changes to inference in a beta backport
That鈥檚 definitely fair enough. I鈥檓 just sad we didn鈥檛 catch all this sooner.
Brainstorming:
Box::new(x) has an expected type of Box<dyn Error> and returns a value of type Box<?T>?T = dyn Error would be helpfulx with a type of ! being coerced to dyn ErrorSome(foo) being coerced into Option<fn()> etcSome options:
?T: SizedBox::new to take unsized values in the futureT: Sized predicatedyn Trait types, on the premise that a more specific type is better and we can always coerce into the dyn Trait laterI'm not sure what other options there are, but I guess that trying to disable the back-propagation in the event that it generates constraints that violate T: Sized seems potentially reasonable, though I don't like the side-effect that making type parameters become T: ?Sized would break callers. (This would be true not just for Box::new, of course.) The only saving grace is that maybe in the future we will come up with better coercion mechanisms that avoid this downside.
I'm also a bit unclear: would we check for any unifications that violate T: Sized? What if the back-propagation is useful sometimes but not others?
(cc @eddyb for thoughts)
we might expect Box::new to take unsized values in the future
If so, back-propagation becomes a non-issue in this case, doesn鈥檛 it?
Another point in the solution space would be to make ! automatically implement all traits whose possible implementation is uniquely determined by the fact that Self = !, as @canndrew has previously proposed. That is, all traits where every method takes Self (that is, !) as an argument, and where therefore all of them are actually uncallable. Notably, I think this encompasses every object-safe trait. (Unless where Self: Sized methods throw a wrench into the works?)
Now, on some level we might abstractly think, "but ! should on principle coerce to any type, and object-safe traits would only be covering one set of cases"; but types involving dyn Trait can of course only exist for object-safe traits, so (again, unless where Self: Sized messes it up), this would in theory solve the whole problem.
(This sounds like a more radical solution, but I dunno. It's something we've (at least in my impression) only held off on in the name of conservatism and incrementality. The idea itself seems "obviously correct", and like something we'd surely want to have eventually, whereas it doesn't seem obviously true that we'd want to have the special-case type inference hacks if we already had this. So it's at least not self-evident which one is less conservative.)
@glaebhoerl but ! already implements Error so automatically implementing more traits does not solve the current problem -- https://github.com/rust-lang/rust/issues/49593#issuecomment-377974905.
Oh, I see. Thanks for explaining. In that case, we only need to implement https://github.com/rust-lang/rust/issues/48055 :P
assigning to self to try to ensure I actually attack this problem tomorrow. But leaving niko on assignee list in case I do not finish it tomorrow (because he and I will be swapping our PTO-states)