rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.53k stars 12.47k forks source link

Tracking issue for RFC 1937: `?` in `main` #43301

Closed aturon closed 1 year ago

aturon commented 7 years ago

This is a tracking issue for the RFC "? in main" (rust-lang/rfcs#1937).

Steps:

Stabilizations:

Related issues:

Unresolved questions:

ghost commented 7 years ago

How are exit statuses going to be dealt with?

ketsuban commented 7 years ago

This comment by @Screwtapello seems to have been made too close to the end of FCP for any alterations to be made to the RFC in response to it.

In short: the RFC proposes returning 2 on failure on grounds which, while well-founded, are obscure and produce a slightly unusual result; the least surprising thing is to return 1 when the program has no indication it wants any more detail than just success or failure. Is this sufficiently bikesheddy that it can be discussed without it feeling like we're perverting the RFC process, or are we now locked into this specific implementation detail?

ghost commented 7 years ago

It's not an implementation detail though, is it?

Some scripts use exit codes as a way to get information from a sub-process.

Screwtapello commented 7 years ago

This is specifically about the case when a sub-process (implemented in Rust) has no information to give, beyond a binary "everything's fine"/"something went wrong".

NoraCodes commented 7 years ago

Some scripts use exit codes as a way to get information from a sub-process.

That behavior is always extremely dependent on the program being called except in that non-zero means failure. Given that std::process::exit with a main-function wrapper and a lookup table is going to remain the best option for those who want a more articulate exit status no matter what is done, this seems like a mostly insignificant detail.

ghost commented 7 years ago

I don't think SemVer has a "mostly insignificant detail" exception though.

glaebhoerl commented 7 years ago

I think the exit code should be added to the unresolved questions list. @zackw also opened a related internals thread.

liigo commented 7 years ago

Many people agree that the exit code should be 1 on failure (instead of 2): https://www.reddit.com/r/rust/comments/6nxg6t/the_rfc_using_in_main_just_got_merged/

bkchr commented 6 years ago

@arielb1 are you going to implement this rfc?

arielb1 commented 6 years ago

@bkchr

No, just to mentor it. I assigned so I won't forget to write the mentoring notes.

bkchr commented 6 years ago

Ahh nice, I would be interested in doing it :) But, I don't have any idea where to start :D

arielb1 commented 6 years ago

@bkchr

That's why I'm here :-). I should write the mentoring instructions soon enough.

bkchr commented 6 years ago

Okay, then I'm waiting for your instructions.

arielb1 commented 6 years ago

Mentoring Instructions

This is a WG-compiler-middle issue. If you want to seek help, you can join #rustc on irc.mozilla.org (I'm arielby) or https://gitter.im/rust-impl-period/WG-compiler-middle (I'm @arielb1 there).

There's a WIP compiler readme at #44505 - it describes some things in the compiler.

Work plan for this RFC:

add the Termination lang-item to libcore

First, you need to add the Termination trait to libcore/ops/termination.rs, along with some documentation. You'll also need to mark it as unstable with an #[unstable(feature = "termination_trait", issue = "0")] attribute - this will prevent people from using it before it is stabilized.

Then, you need to mark it as a lang-item in src/librustc/middle/lang_items.rs. This means the compiler can find it out when type-checking main (e.g. see 0c3ac648f85cca1e8dd89dfff727a422bc1897a6). That means:

  1. adding it to the lang-items list (in librustc/middle/lang_items.rs)
  2. adding a #[cfg_attr(not(stage0), lang = "termination")] to the Termination trait. The reason you can't just add a #[lang = "termination"] attribute is because the "stage0" compiler (during bootstrapping) won't know termination is something that exists, so it won't be able to compile libstd. We'll manually remove the cfg_attr when we update the stage0 compiler. See bootstrapping docs at XXX for details.

allow using Termination in main

This is the interesting part I know how to deal with. This means making a main that returns () not type-check (currently you get a main function has wrong type error) and work.

To make it type-check, you first need to remove the existing error in: https://github.com/rust-lang/rust/blob/9a00f3cc306f2f79bfbd54f1986d8ca7a74f6661/src/librustc_typeck/lib.rs#L171-L218

Then, you need to add a check that the return type implements the Termination trait in (you add a trait obligation using register_predicate_obligation - search for uses of that). That can be done here: https://github.com/rust-lang/rust/blob/9a00f3cc306f2f79bfbd54f1986d8ca7a74f6661/src/librustc_typeck/check/mod.rs#L1100-L1108

The other part is making it work. That should be rather easy. As the RFC says, you want to make lang_start generic over the return type.

lang_start is currently defined here: https://github.com/rust-lang/rust/blob/9a00f3cc306f2f79bfbd54f1986d8ca7a74f6661/src/libstd/rt.rs#L32

So you'll need to change it to be generic and match the RFC:

#[lang = "start"]
fn lang_start<T: Termination>
    (main: fn() -> T, argc: isize, argv: *const *const u8) -> !
{
    use panic;
    use sys;
    use sys_common;
    use sys_common::thread_info;
    use thread::Thread;

    sys::init();

    sys::process::exit(unsafe {
        let main_guard = sys::thread::guard::init();
        sys::stack_overflow::init();

        // Next, set up the current Thread with the guard information we just
        // created. Note that this isn't necessary in general for new threads,
        // but we just do this to name the main thread and to give it correct
        // info about the stack bounds.
        let thread = Thread::new(Some("main".to_owned()));
        thread_info::set(main_guard, thread);

        // Store our args if necessary in a squirreled away location
        sys::args::init(argc, argv);

        // Let's run some code!
        let exitcode = panic::catch_unwind(|| main().report())
            .unwrap_or(101);

        sys_common::cleanup();
        exitcode
    });
}

And then you'll need to call it from create_entry_fn. Currently, it instantiates a monomorphic lang_start using Instance::mono, and you'll need to change it to use monomorphize::resolve with the right substs.

https://github.com/rust-lang/rust/blob/9a00f3cc306f2f79bfbd54f1986d8ca7a74f6661/src/librustc_trans/base.rs#L697

allow using Termination in doctests

I don't really understand how doctests work. Maybe ask @alexcrichton (that's what I would do)?

allow using Termination in #[test]

I don't really understand how libtest works. Maybe ask @alexcrichton (that's what I would do)? Unit tests are basically generated by a macro, so you need to change that macro, or its caller, to handle return types that are not ().

arielb1 commented 6 years ago

@bkchr

Can you at least join the IRC/gitter?

nikomatsakis commented 6 years ago

@bkchr just checking in -- I saw you and @arielb1 were conversing on gitter some time back, any progress? Get suck somewhere?

bkchr commented 6 years ago

No sorry, no progress up to now. Currently I have a lot of things to do, but I hope that I will find some time this week to start on this.

Freyskeyd commented 6 years ago

@bkchr If you need some help let me know!

bkchr commented 6 years ago

I'm currently a little bit stuck, I want to create the Obligation. To create the Obligation I need a TraifRef, for a TraitRef I need a DefId. Can someone point me to some code on how to create a DefId from the Termination Trait?

eddyb commented 6 years ago

@bkchr The trait should be added to the lang item list, e.g.: https://github.com/rust-lang/rust/blob/ade0b01ebf18550e41d24c6e36f91afaccd7f389/src/librustc/middle/lang_items.rs#L312 and get marked with #[termination_trait], e.g.: https://github.com/rust-lang/rust/blob/ade0b01ebf18550e41d24c6e36f91afaccd7f389/src/libcore/fmt/mod.rs#L525-L526

bkchr commented 6 years ago

Yeah that is not the problem, I already done that. I need to check for the termination trait in the check_fn function. I want to use register_predicate_obligation and for that I need the defid of the termination trait.

eddyb commented 6 years ago

Oh, then all you need is tcx.require_lang_item(TerminationTraitLangItem).

nikomatsakis commented 6 years ago

@bkchr how goes? Just checking in again. =) No worries if you are busy, just want to make sure you're getting all the help you need.

bkchr commented 6 years ago

Sorry, busy at the moment :/ Up to now, I got all the help I needed :)

bkchr commented 6 years ago

This is the code to check for the TerminationTrait: https://github.com/bkchr/rust/blob/f185e355d8970c3350269ddbc6dfe3b8f678dc44/src/librustc_typeck/check/mod.rs#L1108

I think that I not checking on the return type of the function? I get the following error:

error[E0277]: the trait bound `Self: std::ops::Termination` is not satisfied
  --> src/rustc/rustc.rs:15:11
   |
15 | fn main() { rustc_driver::main() }
   |           ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Termination` is not implemented for `Self`
   |
   = help: consider adding a `where Self: std::ops::Termination` bound

What does I need to change, to check on the return type of the function?

bstrie commented 6 years ago

@bkchr I'd recommend joining the compiler-middle working group gitter at https://gitter.im/rust-impl-period/WG-compiler-middle for feedback, as well as trying the #rust-internals IRC channel at https://chat.mibbit.com/?server=irc.mozilla.org%3A%2B6697&channel=%23rust-internals . :)

bkchr commented 6 years ago

@bstrie yeah thanks, I'm already part of the gitter chat and could solve my problem. :)

nikomatsakis commented 6 years ago

@bkchr your problem is on this line. The trait reference you want to build there is something like R: Termination where R is the return type of the function. This is specified by building up an appropriate "substs", which is the set of values to substitute for the trait's type parameters (in this case, Self).

However, you are invoking the Substs::identity_for_item method on the trait. This will give you back the substitutions one would use inside the trait definition itself. i.e., in this case you are mapping the Self parameter declared on the Termination trait to Self. This would be appropriate if you were checking the definition of some function inside the Terminator trait, but not so much here.

What you want instead is to get the return type of the entry function. This is just one of the variables ret_ty or actual_ret_ty. Either is fine, but I guess ret_ty is better -- that corresponds to the return type that the user declared (whereas actual_ret_ty is the type the actual code returned).

You can make the substs you want by just calling the mk_substs() method from the tcx. In this case, there is only one parameter, the type, so something like let substs = fcx.tcx.mk_substs(&[ret_ty]); would work, I think.

eddyb commented 6 years ago

I believe the thing to use is tcx.mk_substs_trait(ret_ty, &[]).

nikomatsakis commented 6 years ago

@bkchr just checking in -- had a chance to put that advice to use? (Also, for faster responses, it may be wise to ask on gitter.)

bkchr commented 6 years ago

Yeah, I could solve the problem with gitter :)

nikomatsakis commented 6 years ago

@bkchr how goes? Just checking in.

bkchr commented 6 years ago

Everything okay, I will probably get some time this week to look into the code.

U007D commented 6 years ago

Is there room for one more person to help with this? I would like to begin contributing to the Rust community before year's end and would love to help with this feature. Hopefully it wouldn't be too confusing to have two people collaborating on this.

arielb1 commented 6 years ago

@U007D

This is a small feature and @bkchr is almost done with it.

U007D commented 6 years ago

Ah, ok--that's good to know, thanks. I'll keep an eye out for something else I can help with.

lnicola commented 6 years ago

@U007D Have you seen https://www.rustaceans.org/findwork ?

U007D commented 6 years ago

@lnicola Yes, I have! I'm trying to find something at the intersection of something I feel confident about being able to work on (ie. be a net positive) and I'm passionate about. To be honest, even though I've been learning Rust for about a year, it's still a little intimidating to step up to volunteer for something. FWIW, that is by no means the fault of the Rust community--the Rust community has bent over backwards to make this an open, welcoming and inclusive culture--the best I've had the pleasure of experiencing. (I suspect it has more to do with old battle scars from years and years of experience in the tech industry where teams tend to be competitive rather than collaborative.)

Anyway, it's my goal to pick something this year and to at least begin making a positive contribution. It's time for me to get involved! :)

Thanks for the suggestion, @lnicola. That is a good resource.

Manishearth commented 6 years ago

@bkchr any updates?

bkchr commented 6 years ago

I'm on it (https://github.com/rust-lang/rust/pull/46479). Now I have holidays and time to work in the comments in the pull request. Sorry for all the delays :/

Manishearth commented 6 years ago

Oh, sorry, didn't notice you had a pull request up. Cross-linked it.

jdahlstrom commented 6 years ago

Hello, uhm. So I thought I'd start my potential Rust contributor career by bikeshedding, as is tradition. Specifically, about this one:

How about Exit? It's short and to the point, fitting existing Rust vocabulary. Exit-as-a-noun is a natural counterpart to exit-as-a-verb which, to most, is the familiar word for ending a process "from inside" in a controlled manner.

To a C++ programmer specifically, "termination" brings to mind std::terminate which defaults to abnormal termination (calling abort) and is basically the C++ equivalent to a panic (but unlike a panic, never unwinds the stack).

Manishearth commented 6 years ago

Wait, ignore that comment, looks like the RFC left that explicitly open to discussion.

I do like Exit as a trait name.

scottmcm commented 6 years ago

I'm figuring that the feature will get stabilized far before the trait does, like happened with Carrier.

jdahlstrom commented 6 years ago

FWIW that's another case where I'm really glad that the provisional name was changed before stabilization :D

zackw commented 6 years ago

As the author of the RFC I have no objection to changing the name of the trait to Exit, or anything else really. I'm not particularly good at naming things and happy for someone else to have a better idea.

kennytm commented 6 years ago

https://github.com/rust-lang/rust/blob/5f7aeaf6e2b90e247a2d194d7bc0b642b287fc16/src/libstd/lib.rs#L507

Is the trait supposed to be

  1. placed in libstd instead of libcore, and
  2. just called std::Termination, not std::ops::Termination?
bkchr commented 6 years ago

The trait could not be placed into libcore, because the implementation for Result requires to print to stderr and that can not be done in libcore.

kennytm commented 6 years ago

@bkchr The impl being in libstd doesn't mean the trait needs to be in libstd as well.

bkchr commented 6 years ago

@kennytm I know, but Result is also defined in libcore, so Termination can not be implemented for Result in libstd.