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

Tracking issue for promoting `!` to a type (RFC 1216) #35121

Open
nikomatsakis opened this issue Jul 29, 2016 · 260 comments
Open

Tracking issue for promoting `!` to a type (RFC 1216) #35121

nikomatsakis opened this issue Jul 29, 2016 · 260 comments

Comments

@nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented Jul 29, 2016

Tracking issue for rust-lang/rfcs#1216, which promotes ! to a type.

Pending issues to resolve

  • Regressions from fallback to () #67225
  • How to handle fallback of diverging expressions #65992

Interesting events and links

@canndrew
Copy link
Contributor

@canndrew canndrew commented Jul 30, 2016

Huzzah!

There's a WIP implementation of this here: https://github.com/canndrew/rust/tree/bang_type_coerced

It's current status is: it builds with old-trans and is usable but has a couple of failing tests. Some tests fail due to a bug that causes code like if (return) {} to crash during trans. The other tests are to do with link-time-optimization and have always been buggy for me, so I don't know if they have anything to do with my changes.

My current roadmap is:

  • Get it working with MIR. This hopefully won't be too hard as this is how I started implementing it, but I've been having a problem where MIR builds segfault during compilation.
  • Purge the obsolete divergence stuff from the compiler (FnOutput, FnDiverging and related).
  • Hide the new type behind a feature gate. This would mean, when the feature is disabled:
    • ! can only be parsed as a type in the return position.
    • Diverging type variables default to ().
  • Figure out how we can raise compatibility warnings when a defaulted () gets used to resolve a trait. One way to do this could be to add a new type to the AST called DefaultedUnit. This type behaves like () and will turn into () under some circumstances but raises a warning when it resolves a trait (as ()). The problem with this approach is I think it will be hard to catch and fix all the bugs with the implementation - we'd end up breaking people's code in order to save their code from being broken.

Is there anything that needs to be added to this list? Is it just going to be me working on this? And should this branch be moved onto the main repository?

@chris-morgan chris-morgan mentioned this issue Aug 1, 2016
1 of 3 tasks complete
@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 1, 2016

Figure out how we can raise compatibility warnings when a defaulted () gets used to resolve a trait. One way to do this could be to add a new type to the AST called DefaultedUnit. This type behaves like () and will turn into () under some circumstances but raises a warning when it resolves a trait (as ()). The problem with this approach is I think it will be hard to catch and fix all the bugs with the implementation - we'd end up breaking people's code in order to save their code from being broken.

@eddyb, @arielb1, @anyone_else: Thoughts on this approach? I'm pretty much up to this stage (sans a couple of failing tests that I'm (very slowly) trying to fix).

dns2utf8 added a commit to dns2utf8/rust that referenced this issue Aug 4, 2016
dns2utf8 added a commit to dns2utf8/rust that referenced this issue Aug 4, 2016
@nikomatsakis nikomatsakis added the T-libs label Aug 10, 2016
@tomaka
Copy link
Contributor

@tomaka tomaka commented Aug 11, 2016

What traits should we implement for !? The initial PR #35162 includes Ord and a few others.

Shouldn't ! automatically implement all traits?

This kind of code is fairly common:

trait Baz { ... }

trait Foo {
    type Bar: Baz;

    fn do_something(&self) -> Self::Bar;
}

I'd expect ! to be usable for Foo::Bar in order to indicate that a Bar can never actually exist:

impl Foo for MyStruct {
    type Bar = !;
    fn do_something(&self) -> ! { panic!() }
}

But this is only possible if ! implements all traits.

@suhr
Copy link

@suhr suhr commented Aug 11, 2016

@tomaka There's RFC about it: rust-lang/rfcs#1637

The problem is that if ! implements Trait it also should implement !Trait...

@tomaka
Copy link
Contributor

@tomaka tomaka commented Aug 11, 2016

The problem is that if ! implements Trait it also should implement !Trait...

Then special-case ! so that it ignores any trait requirement?

@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 11, 2016

@tomaka ! can't automatically implement all traits because traits can have static methods and associated types/consts. It can automatically implement traits that just have non-static methods though (ie. methods that take a Self).

As for !Trait, someone suggested that ! could auto-implement both Trait and !Trait. I'm not sure whether that's sound but I suspect that negative traits aren't sound at all.

@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 11, 2016

But yes, it might be nice if ! could auto-implement Baz in the example you gave for precisely these sorts of cases.

dns2utf8 added a commit to dns2utf8/rust that referenced this issue Aug 15, 2016
@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 19, 2016

When exactly do we default diverging type variables to ()/! and when do we throw an error about not being able to infer enough type information? Is this specified anywhere? I'd like to be able to compile the following code:

let Ok(x) = Ok("hello");

But the first error I get is "unable to infer enough type information about _". In this case I think it would make sense for _ to default to !. However when I was writing tests around defaulting behaviour I found it surprisingly difficult to make a type variable default. That's why these tests are so convoluted.

I'd like to have a clear idea of exactly why we have this defaulting behaviour and when it's supposed to get invoked.

@tomaka
Copy link
Contributor

@tomaka tomaka commented Aug 19, 2016

But the first error I get is "unable to infer enough type information about _". In this case I think it would make sense for _ to default to !. However when I was writing tests around defaulting behaviour I found it surprisingly difficult to make a type variable default. That's why these tests are so convoluted.

That's a very good idea in my opinion. Same for None which would default to Option<!> for example.

dns2utf8 added a commit to dns2utf8/rust that referenced this issue Aug 20, 2016
@arielb1
Copy link
Contributor

@arielb1 arielb1 commented Aug 20, 2016

@carllerche

The unit_fallback test is certainly an odd way to demonstrate it. A less-macro-ey version is

trait Balls: Default {}
impl Balls for () {}

struct Flah;

impl Flah {
    fn flah<T: Balls>(&self) -> T {
        Default::default()
    }
}

fn doit(cond: bool) {
    let _ = if cond {
        Flah.flah()
    } else {
        return
    };
}

fn main() {
    let _ = doit(true);
}

Only the type variable created by return/break/panic!() defaults to anything.

@nikomatsakis
Copy link
Contributor Author

@nikomatsakis nikomatsakis commented Aug 24, 2016

When exactly do we default diverging type variables to ()/! and when do we throw an error about not being able to infer enough type information? Is this specified anywhere?

Define "specified". :) The answer is that certain operations, which are not afaik written down anywhere outside the code, require that the type is known at that point. The most common case is field access (.f) and method dispatch (.f()), but another example is deref (*x), and there is probably one or two more. There are mostly decent reasons for this being required -- generally speaking, there are multiple diverging ways to proceed, and we can't make progress without knowing which one to take. (It would be possible, potentially, to refactor the code so that this need can be registered as a kind of "pending obligation", but it's complicated to do so.)

If you make it all the way to the end of the fn, then we run all pending trait selection operations until a steady state is reached. This is the point where defaults (e.g., i32, etc) are applied. This last part is described in the RFC talking about user-specified default type parameters (though that RFC in general needs work).

@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 27, 2016

#36011
#36038
#35940

Are some bugs to add to the list of pending issues.

@glaebhoerl
Copy link
Contributor

@glaebhoerl glaebhoerl commented Aug 27, 2016

@canndrew those look a bit similar to #12609

@canndrew
Copy link
Contributor

@canndrew canndrew commented Aug 27, 2016

Boy, that's an old bug! But yes I'd say my #36038 is a dupe of that (I thought I'd seen it somewhere before). I don't think ! can really be considered for prime time until that's fixed.

@tikue
Copy link
Contributor

@tikue tikue commented Aug 29, 2016

Is it planned for ! to affect pattern matching exhaustiveness? Example of current, possibly-wrong behavior:

#![feature(never_type)]

fn main() {
    let result: Result<_, !> = Ok(1);
    match result {
//        ^^^^^^ pattern `Err(_)` not covered
        Ok(i) => println!("{}", i),
    }
}
@lambda-fairy
Copy link
Contributor

@lambda-fairy lambda-fairy commented Aug 29, 2016

@tikue yes, it's one of the bugs listed above.

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
@GrayJack
Copy link
Contributor

@GrayJack GrayJack commented Aug 15, 2019

What is the state of this one?

@SimonSapin
Copy link
Contributor

@SimonSapin SimonSapin commented Aug 16, 2019

As far as I know the status has not significantly changed since my summary in #57012 (comment).

bors added a commit that referenced this issue Sep 24, 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
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
Centril added a commit to Centril/rust that referenced this issue Nov 21, 2019
…li-obk

Stabilize `!` in Rust 1.41.0

This PR stabilizes the `never_type` (written `!`). The type represents computations that we know diverge in the type system and therefore has no values / inhabitants / elements / members.

The current nightly version is 1.40.0 which will become stable on 2019-12-19.

Tracking issue: rust-lang#35121.
Closes rust-lang#57012.
Closes rust-lang#58184.
Original stabilization report: rust-lang#57012 (comment)

Additional notes:

- In rust-lang#62661 we reserved `impl<T> From<!> for T` so this concern should be resolved.
- The type inference fallback change is moved to `#![feature(never_type_fallback)]` (rust-lang#65992).
- You can find all of the tests referencing `never_type` in this PR which also reorganizes these tests whereas they were more scattered before.

r? @nikomatsakis
@valkum valkum mentioned this issue Dec 11, 2019
1 of 15 tasks complete
yvt added a commit to yvt/yfft-rs that referenced this issue Jan 6, 2020
- The `macro_reexport` feature was removed and superseded by
  `use_extern_macros`. <rust-lang/rust#29638>
- Switch to the version of `stdsimd` provided directly by the toolchain
  rather than one distributed via crates.io or github.io
- `never_type` is unstable again
  <rust-lang/rust#35121>
- `cfg_target_feature` is stable since 1.27.0
- `target_feature` is stable since 1.27.0
yvt added a commit to yvt/ngspades that referenced this issue Apr 25, 2020
- The `macro_reexport` feature was removed and superseded by
  `use_extern_macros`. <rust-lang/rust#29638>
- Switch to the version of `stdsimd` provided directly by the toolchain
  rather than one distributed via crates.io or github.io
- `never_type` is unstable again
  <rust-lang/rust#35121>
- `cfg_target_feature` is stable since 1.27.0
- `target_feature` is stable since 1.27.0
@hosunrise
Copy link

@hosunrise hosunrise commented Jun 12, 2020

What is the state of this one?

@Aaron1011
Copy link
Member

@Aaron1011 Aaron1011 commented Jun 13, 2020

@hosunrise: The is currently blocked on #67225

@Fishrock123
Copy link
Contributor

@Fishrock123 Fishrock123 commented Jul 16, 2020

I may be way off here but the specific issues that the lint discussion (#66173) mentions both seem solvable if every enum has a ! branch in the type system?

I will note that does not apply to the issue mentioned in the OP of #67225, which would still be an issue.

@tema3210
Copy link

@tema3210 tema3210 commented Aug 14, 2020

I may be way off here but the specific issues that the lint discussion (#66173) mentions both seem solvable if every enum has a ! branch in the type system?

Actually, every enum can be threated as there was infinite number of variants that can never occur, and thus they all aren't worth of mentioning.

@nikomatsakis
Copy link
Contributor Author

@nikomatsakis nikomatsakis commented Nov 24, 2020

I've opened #79366 which introduces a modified form of fallback for !. I believe it addresses the problems we encountered in the first round and may indeed pave the way for eventual stabilization. In particular, it avoids both the unsound changes to type inference (by sometimes defaulting to ()) and the failed compilations (by sometimes defaulting to !). (It can still introduce regressions where code no longer compiles, though, so we ought to do some crater runs.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.