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
2 tasks
nikomatsakis opened this issue Jul 29, 2016 · 354 comments
Open
2 tasks

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

nikomatsakis opened this issue Jul 29, 2016 · 354 comments
Assignees
Labels
A-typesystem Area: The type system B-RFC-approved Feature: Approved by a merged RFC but not yet implemented. B-unstable Feature: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-never_type `#![feature(never_type)]` finished-final-comment-period The final comment period is finished for this PR / Issue. Libs-Tracked Libs issues that are tracked on the team's project board. S-tracking-design-concerns Status: There are blocking ❌ design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Jul 29, 2016

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

Pending issues to resolve

Interesting events and links

@nikomatsakis nikomatsakis added B-RFC-approved Feature: Approved by a merged RFC but not yet implemented. T-lang Relevant to the language team, which will review and decide on the PR/issue. B-unstable Feature: Implemented in the nightly compiler and unstable. labels Jul 29, 2016
@canndrew
Copy link
Contributor

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?

@canndrew
Copy link
Contributor

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).

@nikomatsakis nikomatsakis added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Aug 10, 2016
@tomaka
Copy link
Contributor

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 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 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

@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

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

@canndrew
Copy link
Contributor

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 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.

@arielb1
Copy link
Contributor

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

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

#36011
#36038
#35940

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

@glaebhoerl
Copy link
Contributor

@canndrew those look a bit similar to #12609

@canndrew
Copy link
Contributor

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 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

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

@tmandry tmandry added the I-lang-nominated The issue / PR has been nominated for discussion during a lang team meeting. label Dec 20, 2023
@traviscross
Copy link
Contributor

@rustbot labels -I-lang-nominated

We discussed this in the triage meeting today, mostly to understand where this might be with respect to the Rust 2024 edition.

@compiler-errors is working with @WaffleLapkin on adding a lint to detect the cases of fallback that have made the never type difficult to stabilize. E.g.:

if false { panic!() } else { transmute::<_ /* ?0 */>(()) }
// later in fallback:     ?0 := ()
// after ! stabilization: ?0 := !

We're excited by this work and hope to see it land.

@rustbot rustbot removed the I-lang-nominated The issue / PR has been nominated for discussion during a lang team meeting. label Dec 20, 2023
@simensgreen
Copy link

Can we let the type ! Implement all traits at all?

#![feature(never_type)]
#![feature(never_type_fallback)]

trait Foo {}

fn foo<T: Foo>(t: T) {}

fn main() {
    foo(panic!());
//      ^^^^^^^^
//      | the trait `Foo` is not implemented for `!`
}

Theoretically, this code could not exist due to value of type ! can't be ever created.
Such a change would allow no additional code snippets to be written for custom traits.

@Nadrieril
Copy link
Member

Nadrieril commented Jan 7, 2024

There's at least two cases where we can't do it automatically:

  • traits that return Self, such as Default;
  • traits like Deref that have associated types/constant, because we'd need to choose a type/constant.

In general, the only things we could write automatically are functions that take an empty type as argument.

@fmease
Copy link
Member

fmease commented Jan 7, 2024

Can we let the type ! Implement all traits at all?

@simensgreen We very likely won't ever do that. See #113875 for more context. That issue is assigned to me since I plan on adding a paragraph to the Reference documenting this languages/types guarantee but I still haven't gotten around to do that.

@jonhoo
Copy link
Contributor

jonhoo commented Jan 17, 2024

Very excited about this nearing stabilization!
Just for cross-linking purposes, I suspect that once this stabilizes we can probably also stabilize #61695 soon thereafter.

@ProgramCrafter
Copy link

Can we let the type ! Implement all traits at all?

Maybe create attribute #[impl_for_never] or #[impl_for_uninhabited(!)]?

It may as well take associated types:

#[impl_for_uninhabited(!, Item=u32, Sentinel=0)]
trait T { ... }

@Kixunil
Copy link
Contributor

Kixunil commented Jan 17, 2024

I believe any non-pub trait without static items, without associated types and constants and any such pub trait that is not sealed could be implemented for ! out of the box. If there's an attribute, just write a proc macro.

@clarfonthey
Copy link
Contributor

#[impl_for_uninhabited(!, Item=u32, Sentinel=0)]
trait T { ... }

This doesn't seem any more useful than just writing the implementation by hand.

@zopsicle
Copy link
Contributor

zopsicle commented Jan 17, 2024

I don’t think opt-in derived impls for ! are relevant to this RFC. This can be implemented backwards-compatibly at any time after stabilization of ! as a type.

@ids1024
Copy link
Contributor

ids1024 commented Jan 17, 2024

This doesn't seem any more useful than just writing the implementation by hand.

It could be a fair bit less code than manually implementing each method for a trait with a several methods.

But the attribute suggested here could just be a proc macro.

@Kixunil
Copy link
Contributor

Kixunil commented Jan 18, 2024

I don’t think opt-in derived impls for ! are relevant to this RFC. This can be implemented backwards-compatibly at any time after stabilization of ! as a type.

Not backward-compatibly if it implements things out-of-the-box for eligible types. However I'd rather have this sooner and use an attribute in the future.

@Fishrock123
Copy link
Contributor

Fishrock123 commented Jan 23, 2024

@nikomatsakis could you update the OP to reflect #35121 (comment)?

(Edit: the below is an unrelated thing that I linked to the wrong url)

@Ciel-MC
Copy link

Ciel-MC commented Feb 4, 2024

Hey, I notice that all the issues mentioned at blockers have been closed, do you think this can be stabilized? Or are there some undocumented blockers rn?

@bjorn3
Copy link
Member

bjorn3 commented Feb 4, 2024

The second issue was actually closed in favor of this issue. In other words, the actual problem wasn't fixed yet, but it wasn't seen as worthwhile to have a issue separate from this one tracking the problem.

@Ciel-MC
Copy link

Ciel-MC commented Feb 4, 2024

Im not quite grasping what the problem is, and there didn’t seem to be an example in the original issue, is there perhaps a concrete example somewhere?

@slanterns
Copy link
Contributor

Im not quite grasping what the problem is, and there didn’t seem to be an example in the original issue, is there perhaps a concrete example somewhere?

#67225 (comment)

#66757 (comment)

@TheLostLambda
Copy link

I might also cross-link this to #64715, which should at least be partially unblocked by the stabilization of ! here!

Once type Infallible = !, then fixing #64715 will finally mean I don't have to write silly code like the below!

impl From<Infallible> for AtomicLookupError {
    fn from(_: Infallible) -> Self {
        unreachable!()
    }
}

@WaffleLapkin
Copy link
Member

@TheLostLambda the most recent update I've heard from T-types is that making an impl<T> From<!> for T { ... } is not possible with the current state of the trait solver (as it does not have a way to handle overlapping impls like that), so I wouldn't get your hopes up too much.

@coolCucumber-cat
Copy link

What issues are holding this up right now? All the issues about bugs seem to be closed now, what do we still need to do?

One use case for this would be when you have multiple fallible try_into conversions but when one of them may not actually be fallible. In that case, the error type for the infallible conversions is Infallible and the fallible ones are something else, so it doesn't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-typesystem Area: The type system B-RFC-approved Feature: Approved by a merged RFC but not yet implemented. B-unstable Feature: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-never_type `#![feature(never_type)]` finished-final-comment-period The final comment period is finished for this PR / Issue. Libs-Tracked Libs issues that are tracked on the team's project board. S-tracking-design-concerns Status: There are blocking ❌ design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
Status: Idea
Development

No branches or pull requests