Skip to content
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

Stabilize never_type *again* #57012

Closed
cramertj opened this issue Dec 20, 2018 · 71 comments · Fixed by #65355
Closed

Stabilize never_type *again* #57012

cramertj opened this issue Dec 20, 2018 · 71 comments · Fixed by #65355
Labels
T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@cramertj
Copy link
Member

cramertj commented Dec 20, 2018

#49593 has now been fixed. This was the reason for the previous revert of stabilization (#50121). Previous stabilization report is [here]. Let's try this again!

@rfcbot fcp merge
cc #35121

(pnkfelix notes :the previous issue discussing stabilization of ! was #48950; its possible the [here] above was supposed to link to that...)

@cramertj cramertj added T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Dec 20, 2018
@cramertj
Copy link
Member Author

@rfcbot fcp merge

c'mon, rfcbot!

@rfcbot
Copy link

rfcbot commented Dec 20, 2018

Team member @cramertj 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.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Dec 20, 2018
@SimonSapin
Copy link
Contributor

SimonSapin commented Dec 20, 2018

@rfcbot concern impls

The remaining unchecked checkbox in the description of #35121 is:

What traits should we implement for !? The initial PR #35162 includes Ord and a few others. This is probably more of a T-libs issue, so I'm adding that tag to the issue.

Stabilizing ! and then (in a) later (release cycle) adding a new impl of existing traits is not a breaking change, is it? If not, this doesn’t need to be a blocker and let’s mark this as resolved immediately.

If it is, let’s discuss it in this issue. Currently in https://doc.rust-lang.org/nightly/std/primitive.never.html we have:

impl Hash for !
impl Copy for !
impl Debug for !
impl Clone for !
impl PartialOrd<!> for !
impl Ord for !
impl Eq for !
impl PartialEq<!> for !
impl Display for !
impl Error for !
impl Termination for !

@rust-lang/libs, anything to add?

@withoutboats
Copy link
Contributor

@SimonSapin it's not a breaking change AFAIK, so we can add them over time.

I'm not sure if we should implement traits for !. Under some coherence related proposals, its possible to write impls guaranteeing that a type will never implement a trait - negative impls, in other words. I don't know the pros and cons of providing positive vs negative impls for ! (which could go either way) if that feature were added to the language.

@SimonSapin
Copy link
Contributor

@rfcbot concern fallback

In addition to reverting the stabilization, #50121 also reverted “to the previous fallback semantics (i.e. it is once again dependent on whether the crate has opted into #[feature(never_type)]”.

As far as I understand, this is about type inference of a diverging expression like panic!(…), where there is no constraint from surrounding code.

When it previously landed in Nightly, we found out #35121 (comment) that the fallback change could cause some previously-valid code to not compile anymore: #48950 (comment), and some (other) previously-valid unsafe code to now have Undefined Behavior #48950 (comment) (returning Result<!, _>::Ok in reachable/reached code).

@SimonSapin

This comment has been minimized.

@cramertj

This comment has been minimized.

@Ericson2314
Copy link
Contributor

for<E> E: From<!>

Is nice for error handling, but I recall there are orphan/overlapping issues adding it later. I think this is because that impl is technically an overlapping impl with for<A> A: From<A>, so downstream impls that would overlap with it aren't orpans. But if we somehow hack that into core, then the downstream impls can't exist.

@SimonSapin
Copy link
Contributor

@Ericson2314 I agree that impl<T> From<!> for T would be nice to have. Indeed, if we ever add it, that needs to happen before or in the same release cycle as stabilization of !. And as you said, we can’t "just" add it because it overlaps with impl<T> From<T> for T.

Adding "some hack" to bend the language rules and somehow allow both these impls (picking an arbitrary one to "win" is ok, since they behave the same at the intersection) has been discussed before in the abstract, but there has never been a concrete proposal. Concretely, this would need:

  • A plan for how this would work in terms of compiler implementation
  • Some buy-in from the language team that bending the language rules in libcore is not a non-starter
  • Someone who volunteers to implement it

Without all of these things soon, I suspect that consensus would be to give up on impl<T> From<!> for T and stabilize ! without it.

@Centril
Copy link
Contributor

Centril commented Dec 20, 2018

Noting so I/we remember from this week's T-lang meeting:

@rfcbot concern fundamental-and-fn-traits

We had some discussion around Fn* traits (which are #[fundamental]) and !. (Are there other fundamental traits?) @cramertj and @withoutboats had some ideas that would be nice to hear more in-depth about.

@rfcbot concern more-elaborate-report

It would be nice to have all the behavior changes more clearly specified in one place before we stabilize so it's clear what is being stabilized.

@rfcbot concern from-impl

Noting this as a concern temporarily until conversation has resolved itself.

@SimonSapin
Copy link
Contributor

When it previously landed in Nightly, we found out that the fallback change could […]

I meant to add: in #35121 (comment) we discussed only making the fallback change in a new edition. We missed the 2018 train, so this would be for 2021 (or whenever we’ll make the next one).

Also, in the #48950 (comment) case, fallback occurred in the expansion of a macro_rules macro. But macros are not edition-hygienic, are they?

@glaebhoerl
Copy link
Contributor

Adding "some hack" to bend the language rules and somehow allow both these impls (picking an arbitrary one to "win" is ok, since they behave the same at the intersection) has been discussed before in the abstract, but there has never been a concrete proposal. Concretely, this would need:

I don't know whether this would actually be easier to implement than the "some hack" itself which would allow the From impls to coexist, but another option which could unblock this question for now, without forever giving up on the right to add the From<!> impl, would be to add a temporary hack which just forbids downstream crates from writing any impls which would conflict with it (as-if the impl did exist), without actually adding the impl itself in core yet.

To try to avoid confusion I'll call the (hypothetical) hack which allows From<!> and From<T> to coexist the "overlap hack", and the hack which merely forbids downstream from conflicting with it the "future-proofing hack".

Even if it's not easier to implement, the future-proofing hack requires fewer commitments -- we don't have to commit to the From<!> impl existing and being stable (IINM, impls are still insta-stable, exacerbating the whole issue), we don't have to commit to the overlap hack existing and continuing to work in the future, we can revert the future-proofing hack at any point if we decide it's not worth it.

@SimonSapin
Copy link
Contributor

But the future-proofing hack blocks a useful use case.

For the same reason that impl<T> From<!> for T is desirable, if it’s not implemented then impl From<!> for Foo might be desirable as the next best thing.

@glaebhoerl
Copy link
Contributor

Yes, which is why it's good that we can choose to revert it at any time.

@cramertj
Copy link
Member Author

cramertj commented Dec 21, 2018

An alternative in the future would be to add the impl but ignore coherence completely for any From<!> impl and just allow overlap, since all possible implementations are equivalent (we could potentially do the same for any traits where all functions have uninhabited arguments, and there are no non-function associated items).

@SimonSapin
Copy link
Contributor

I like the idea of tweaking coherence not with a special case for a particular pair of impls but with a rule about allowing overlap when the intersection only has unreachable functions. There is somewhat similar precedent in https://rust-lang.github.io/rfcs/1268-allow-overlapping-impls-on-marker-traits.html, although like in that case we might want to make it opt-in.

@cramertj
Copy link
Member Author

@Centril

concern fundamental-and-fn-traits
We had some discussion around Fn* traits (which are #[fundamental]) and !. (Are there other fundamental traits?) @cramertj and @withoutboats had some ideas that would be nice to hear more in-depth about.

TL;DR: We shouldn't implement the Fn traits for !, because they have an associated type and we'd have to choose what it is. One option would be to choose !, but there's no real logical / fundamental reason for this. Instead, providing this implementation would actually prevent users from implementing custom traits for ! where they also wanted to add a blanket impl over all T: Fn.... This seems like a potentially useful thing to do, whereas using ! as a Fn...<...> -> ! or Fn...<...> -> () seems not particularly useful.

@Ericson2314
Copy link
Contributor

@SimonSapin I really like that idea! We could use the same logic that deduces there is only one possible impl to also allow elliding the trait items:

impl<T> From<!> for T;

@SimonSapin
Copy link
Contributor

#57012 (comment)

it's not a breaking change AFAIK, so we can add them over time.

@rfcbot resolve impls


@cramertj How do you feel about making this issue explicitly not propose any change to type inference fallback from () to !, and leave that change to future discussions? This would resolve my "fallback" concern: #57012 (comment)

@SimonSapin
Copy link
Contributor

#57012 (comment)

I like the idea of tweaking coherence not with a special case for a particular pair of impls but with a rule about allowing overlap when the intersection only has unreachable functions.

I believe this would be compatible to add later, after the never type is stabilized. And so this doesn’t need to block this FCP. @Centril, does this resolve your from-impl concern?

@Ericson2314
Copy link
Contributor

@SimonSapin ah so to be clear, if we have Foo: From<!>, and then we add the blanket impl, the Foo impl becomes a harmless overlap (even if it is fully subsumed) by the same analysis.

@SimonSapin

This comment has been minimized.

@SimonSapin
Copy link
Contributor

SimonSapin commented Feb 27, 2019

Since discussion seems to have stalled, I’ll try to summarize the remaining concerns.

  • This FCP proposes two things: stabilizing the never type, and changing type inference fallback. In some rare cases, the latter can change the meaning of previously-valid stable unsafe code and make it unsound: Stabilize never_type *again* #57012 (comment). (Disclosure, I filed this one.) Possible resolutions include:

    • Tweak the proposed inference rules so that they don’t change the meaning of existing code in cases where that would be unsound. It’s not clear if this is possible.
    • Design and implement mitigations that will help project maintainers find out if their code or their dependencies’ code is affected. (Crater is not sufficient since it only tests open-source code that it can find, only on Linux, and only with a fixed set of system packages installed, and only runs unit tests at most.) It’s not clear yet what these mitigations should do exactly.
    • Declare this breaking change to be “minor”, hope that it causes more benign crashes than exploitable vulnerabilities, and let project maintainers deal with it however they can. I personally think this is not acceptable.
    • Stabilize the never type separately (Stabilize (only) the never type #58184) from the inference change, since it is not actually affected by this concern, and keep discussing tweaks or mitigations in another thread.
  • It would be nice to have impl<T> From<!> for T to reflect at the trait level that a “value” of the never type can be trivially converted to any type (since that code is unreachable). However trait coherence does not let us write that impl since it overlaps with the stable identity conversion impl<T> From<T> for T, for T = !. And adding it after the never type has already been stable would be a breaking change: Stabilize never_type *again* #57012 (comment). Possible resolutions include:

    • Lattice impl specialization. (However even “basic” specialization has big remaining design questions at this point.)
    • Extend RFC 1268 “Allow overlapping implementations for marker traits” to also include traits with only methods that take at least one parameter whose type is uninhabited.
    • Hack in a narrower special case of one of the above: have a permanently-unstable language rule just for this one standard library impl
    • Give up on this impl. It would be nice to have, but is it really worth blocking the never type from stabilization for so long?

@nikomatsakis
Copy link
Contributor

Nominating for discussion in lang-team meeting to try and get "unstuck". We discussed a bit today.

I think we generally agreed that, with respect to the second question at least, an appealing option would be to extend coherence so that it was treated "as if" such an impl existed, without actually having the impl. That would require some careful coding, though, from what I can tell.

It would certainly be easier to just allow this impl to "opt out" from coherence rules -- I wonder what it would take to convince ourselves that specialization will allow the impl. (It seems very likely to me that it would, but I've not given deep thought to it.)

Regarding the fallback, there is definitely a "core disagreement" here between (a) missing an opportunity to have the "right" fallback and (b) affecting existing code. I am curious about the "mitigations" option -- @SimonSapin are you thinking of something like a lint?

@Centril
Copy link
Contributor

Centril commented May 30, 2019

We did not have any time to discuss the issue this week on the language team meeting; rescheduling for next week instead.

@Centril
Copy link
Contributor

Centril commented Jun 6, 2019

Postponing until a future meeting.

@dnrusakov
Copy link

dnrusakov commented Jul 9, 2019

@Centril Are there any news on never type stabilization? What is the current status?

@Centril
Copy link
Contributor

Centril commented Jul 9, 2019

@dnrusakov Still the same as before; see #57012 (comment) and #57012 (comment). I think the main blocker now is the future proofing some sort of perma-unstable reservation hack. I'm not familiar with those parts of the compiler to do that, but maybe @eddyb or @arielb1 can.

bors added a commit that referenced this issue Aug 5, 2019
reserve `impl<T> From<!> for T`

this is necessary for never-type stabilization.

cc #57012 #35121

I think we wanted a crater run for this @nikomatsakis?

r? @nikomatsakis
@agausmann
Copy link
Contributor

agausmann commented Aug 16, 2019

How useful / necessary is the trait impl? Conversion from ! to any type T can be trivially done using the empty match statement. I wouldn't mind writing something like result.map_err(|never| match never {})? until this is resolved.

@agausmann
Copy link
Contributor

agausmann commented Aug 16, 2019

I re-read everything and I get it now - avoiding problems from impl<!> for MyType. 👍

Centril added a commit to Centril/rust that referenced this issue Sep 26, 2019
reserve `impl<T> From<!> for T`

this is necessary for never-type stabilization.

cc rust-lang#57012 rust-lang#35121

I think we wanted a crater run for this @nikomatsakis?

r? @nikomatsakis
Centril added a commit to Centril/rust that referenced this issue Sep 26, 2019
reserve `impl<T> From<!> for T`

this is necessary for never-type stabilization.

cc rust-lang#57012 rust-lang#35121

I think we wanted a crater run for this @nikomatsakis?

r? @nikomatsakis
Centril added a commit to Centril/rust that referenced this issue Sep 26, 2019
reserve `impl<T> From<!> for T`

this is necessary for never-type stabilization.

cc rust-lang#57012 rust-lang#35121

I think we wanted a crater run for this @nikomatsakis?

r? @nikomatsakis
bors added a commit that referenced this issue Sep 26, 2019
reserve `impl<T> From<!> for T`

this is necessary for never-type stabilization.

cc #57012 #35121

I think we wanted a crater run for this @nikomatsakis?

r? @nikomatsakis
@crlf0710
Copy link
Member

Now that #62661 is merged, what's next?
@eddyb i believe indirect-coherence-breakage concern can be resolved now?

what can we do on the fallback part?

@Centril
Copy link
Contributor

Centril commented Oct 13, 2019

Canceling FCP here in favor of the one in #65355.

@rfcbot cancel

@rfcbot
Copy link

rfcbot commented Oct 13, 2019

@Centril proposal cancelled.

@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Oct 13, 2019
@bors bors closed this as completed in 0828d53 Nov 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.