Try   HackMD

T-lang meeting agenda

  • Meeting date: 2024-04-03

Attendance

  • People: TC, pnkfelix, Waffle, Santiago, Josh, nikomatsakis, eholk, tmandry, Urgau, Daria

Meeting roles

  • Minutes, driver: TC

Scheduled meetings

  • 2024-04-03: Planning meeting

Edit the schedule here: https://github.com/orgs/rust-lang/projects/31/views/7.

Announcements or custom items

(Meeting attendees, feel free to add items here!)

Guest attendee items

TC: For any guests who are present, please note in this section if you're attending for the purposes of any items on (or off) the agenda in particular.

Moving right along

TC: As we've been doing recently, due to the impressive backlog, I'm going to push the pace a bit. If it's ever too fast or you need a moment before we move on, please raise a hand and we'll pause.

Design meeting at 12:30 EST / 09:30 PST / 17:30 CET

TC: Remember that our design meetings start half an hour earlier than in the past.

RTN

TC: We had a call 2024-03-04 to discuss the syntax of RTN. There was no explicit consensus from that meeting, but the general mood was that there were two axes along which we might find consensus:

  • Axis 1: Whether to say () or (..) as sugar for ::Output.
  • Axis 2: Whether and how to distinguish the type namespace from the value namespace.

The next steps, per NM, are:

  • Author the RFC and kick-start public conversation.
  • Revise write-up focusing on the final decision space (the axes mentioned above, for example).
    • and highlight the shared values that seemed to be popping up in people's write-ups.
  • Niko is mildly curious about gathering up some data e.g., doing some interviewing, I'm doing some thinking about this.

Rust 2024 review

Project board: https://github.com/orgs/rust-lang/projects/43/views/5

None.

Tracking Issue for Lifetime Capture Rules 2024 (RFC 3498) #117587

Link: https://github.com/rust-lang/rust/issues/117587

TC: We accepted the RFC and the implementation has landed for Rust 2024. We do need to stabilize some way of expressing precise capturing. We're close to stabilizing ATPIT.

TC: However, in terms of timeline, landing TAIT is going to be a bit tight. I'm going to write an RFC with a more direct means for precise capturing, e.g. using impl<'t, T> Trait or impl('t, T) Trait syntax. Let's talk about in the planning meeting today and whether we want to schedule a design meeting for this (or whether everyone is already on board).

NM: We probably will want a meeting to talk about it.

tmandry: +1.

Reserve gen keyword in 2024 edition for Iterator generators #3513

Link: https://github.com/rust-lang/rfcs/pull/3513

TC: This RFC has now completed FCP. I'll merge it after doing a pass of final proofreading. The keyword is reserved in nightly Rust 2024 and gen blocks are available with the gen_blocks feature flag.

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

Link: https://github.com/rust-lang/rust/issues/35121

Next meeting

TC: We discussed this in the design meeting on 2024-03-13. We then agreed to meet with Waffle for fallow-up discussions.

That call is now scheduled for 2024-04-05 at 1300 UTC, 9a EDT, 6a PDT. Let's do the call in this same meeting room.

Background work, revised proposal

TC: Waffle and I have been discussing some steps in the path forward. It's making it possible to test the effect of various options discussed in the playground, e.g.:

#![allow(internal_features)]
#![feature(rustc_attrs, never_type)]
#![rustc_never_type_options(
    // no | unit | niko | never
    // default = unit
    // if feature(never_type_fallback) then default = niko
    fallback = "no",
    // unit | never
    // default = never
    //diverging_block_default = "unit",
)]
fn main() {
    match () {
        _ => return,
    };
    //~^ ERROR type annotations needed
    //~| NOTE cannot infer type
}

TC: It's working to do crater runs for some of these options. These steps seem likely to be useful input to the follow on discussions that we'll have.

Waffle also discussed this with tmandry at Rust Nation, and based on that discussion has a more streamlined proposal:

  1. Change fallback to ! on 2024 edition.
  2. Add a lint against fallback affecting a generic that is passed to an unsafe function.
  3. Add a future incompat lint for some/most of the code that will be broken by fallback being !.
  4. Add documentation/migration tools for people adopting 2024 edition.
  5. At a later date when 2024 edition adoption is sizable, make the breaking change to fallback to ! always everywhere.
  6. Change Infallible = !.
  7. Stabilize ! (!).

Exciting news, feedback?

One exciting bit of news is that Waffle has figured out how to lint against the "objc" pattern that prevented prior stabilizations.

#![allow(internal_features)]
#![feature(rustc_attrs, never_type)]
//#![cfg(rustc_never_type_options(fallback="never"))]

pub unsafe fn send_message<R>() -> Result<R, ()> {
    dbg!(core::any::type_name::<R>());
    Ok(unsafe { core::mem::transmute_copy::<_, R>(&[0u8; 128]) })
}

macro_rules! msg_send {
    () => {
        match send_message::<_ /* ?0 */>() {
            Ok(x) => x,
            Err(_) => loop {},
        }
    };
}

fn main() {
    let _: _ = unsafe { msg_send!() };
    //~^ OK or UB?
}

The lint fires against cases where fallback affects a generic argument passed to an unsafe function. This is exciting because (it's believed that) changes to fallback behavior can only cause UB if fallback affects an inferred type that is passed to an unsafe function, as the passed in type might be part of the safety contract.

He's looking for feedback that lang wants this before putting in the work on it.

My own view is that this makes a ton of sense and that we should consider making this lint deny-by-default or a hard error in Rust 2024. Thoughts?

pnkfelix: Is this transitive?

Waffle: No, but that's OK, because a safe function can't have a contract like this.

tmandry: Are there any drawbacks?

Waffle: Not really.

scottmcm: Encouraging people to put the types here sounds like a good thing. It makes sense to me to have this lint. I often wish we had a lint even for i32 fallback.

NM: +1.

scottmcm: It reminds me of how if you're returning a lifetime that didn't come from the caller, that's funky. So this makes lots of sense to me.

tmandry: +1.

Nominated RFCs, PRs, and issues

"Unsafe Extern Blocks" rfcs#3484

Link: https://github.com/rust-lang/rfcs/pull/3484

TC: We met back on 2023-10-11 to discuss this RFC, and in that meeting, we hammered out a consensus for how to move forward on this as articulated by pnkfelix here. We've just been waiting for the RFC to be updated according to that consensus, and it now has been.

We discussed this in the meeting on 2024-03-27, and scottmcm proposed FCP merge:

Updates here look good! I this this is a good path forward and this version addresses the previous worries about changing something to be the opposite over an edition boundary.

TC: How do we feel about moving forward on this for Rust 2024?

NM: I'm +1 on moving forward.

Waffle: I'm worried about some of the recent comments, e.g.:

https://github.com/rust-lang/rfcs/pull/3484#discussion_r1548068355

Is the UB on the definition or on the call?

scottmcm: I think I agree with RalfJ that it may be possible to change what is detectable here, but I think the RFC still covers the main use case here. There are a lot more callers than declarations.

NM: If you have an incorrect signature, then it's incorrect to call but not to define, and if you have two conflicting signatures, then it's unsafe to call either of them, as they may be merged, right?

Waffle: Correct.

NM: So, even though maybe there are bugs here we could fix in LLVM, the RFC still seems right to me, in the sense that the functions may be called, maybe by someone else.

NM: Even though these declarations are unsafe now, the callers are being asked to prove something they cannot prove.

Waffle: That makes sense.

ScottMcM: +1 to niko's point. I can prove that my signature is right; I can't prove that everyone else did it right in crates I don't know about.

scottmcm, pnkfelix, josh: +1 on the proposed FCP.

Consensus: This will go directly into FCP.

NM: Idea for the spec team: It'd be good to write down what unsafe or safe is meant to prove.

"RFC: Reserve unprefixed guarded string literals in Edition 2024" rfcs#3593

Link: https://github.com/rust-lang/rfcs/pull/3593

TC: In RFC 3101 we reserved (starting in Rust 2021) ident#"foo"# but we didn't reserve #"foo"#. This RFC proposes that we reserve #"foo"# also starting in Rust 2024. The motivation is that there are various proposals for new kinds of string literals that may want to use this. But perhaps this RFC could also be justified on its own merits as closing a gap in RFC 3101.

TC: The migration for any affected macro invocations would be to add spaces. So foo!(#"bar"#) would become foo!(# "bar" #) or foo!(# "bar"#).

We discussed this in the meeting on 2024-03-27. Josh has proposed FCP merge, and scottmcm is +1.

TC: What do we think?

Waffle: Yes, please, to this.

tmandry: Sounds right.

NM: This is just reserving syntax, right? No semantics? Seems fine. +1.

josh, pnkfelix, scottmcm, tmandry: +1 on the proposed FCP.

Consensus: This will move directly into FCP.

"Stabilise inline_const" rust#104087

Link: https://github.com/rust-lang/rust/pull/104087

TC: Inline const had been blocked on one case of optimization-dependent errors:

https://github.com/rust-lang/rust/issues/107503

That was blocked on understanding an incremental build time regression. Due to some careful analysis by RalfJ, that is now resolved.

However, there are other cases of optimization dependent errors:

Back in 2023, we had filed a concern on inline const due to optimization-dependent errors:

https://github.com/rust-lang/rust/pull/104087#issuecomment-1449080210

We discussed this situation in the meeting on 2024-03-20 with respect to the new cross-crate inlining issue. We felt that it was a bug, of course, but there was also a feeling that it was distinguishable and did not need to block inline const:

scottmcm: At least for me, there's an arguable distinction here. Since it's dead code, that maybe seems less bad. What we cared about was when you had a const that you really were using.

tmandry: Right, the main concern with optimization-dependent errors was that code that compiled one day would stop compiling on another day. Other than directly due to fixing this bug, that wouldn't be the case here.

tmandry: So this seems like a bad bug, but maybe this doesn't need to be a blocker to inline const.

Once that concern is resolved, inline const will move into FCP, so we wanted to check this proposed consensus with the wider team.

What do we think?

scottmcm: What I'd like to do in this meeting is that, if we're not willing to move forward, to resolve the original concern and to file a new one.

scottmcm: I'm willing to draw a line here due to the fact that, e.g. in the case of the cross-crate inlining, this code is actually unused.

Josh: That seems reasonable to me.

scottmcm: We fixed the value-dependent case. This other one is about functions that are not called at all. This is less inline const specific.

Josh: +1 for going forward on this.

TC: Anyone object to tmandry resolving the 2023 concern?..

TC: Hearing none.

tmandry: I'll resolve that concern and the other related one.

NM: It feels like a reasonable compromise to me to have fewer errors sometimes. It'd be good to have a mode that flags all errors also.

Josh: Would it make sense to amend our previously FCPed policy?

scottmcm: We decided on a policy about cargo check, which says (in my paraphrasing) that check is allowed to skip things that are too expensive to be worth it.

https://github.com/rust-lang/rfcs/pull/3477

scottmcm: Check out: https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=ff5180a02191e2ad213875317e5cac99

pub fn f() {
    g::<{1 << 48}>();
}
pub fn g<const N: usize>() -> [u8; N] {
    [0; N]
}

It compiles in release but not in debug. I'm happy to consider this example a bug. But I just don't think it blocks inline const.

The example from
https://github.com/rust-lang/rust/issues/122814#issuecomment-2015081001
is
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6a4f64d6bcccbca1a500ac89c428214c
which does give an "unreachable expression" warning.


TC: Separately, on the tracking issue, we had marked as a blocker to inline const the fact that the expr macro fragment specifier had diverged from the grammar:

https://github.com/rust-lang/rust/issues/86730

Since then, we've accepted a policy for how to handle these cases in RFC 3531:

https://github.com/rust-lang/rfcs/pull/3531

Someone needs to apply this policy to the expr fragment specifier, and I've reached out to WG-macros to see whether they're interested in adopting this work.

How do we want to handle this with respect to stabilizing inline const?

NM: I'm happy with the policy and it sounds like we need someone to do the work.

Josh: I don't see this as a blocker to inline const.

scottmcm, tmandry, pnkfelix, NM: +1.

eholk: I just pinged Vincenzo from wg-macros about this issue. He's been looking for an easy area to start contributing to the compiler and this seems like something that might work for him.

Consensus: We won't treat this as a blocker for inline const.

(The meeting ended here.)


"debuginfo: Stabilize -Z debug-macros, -Z collapse-macro-debuginfo and #[collapse_debuginfo]" rust#120845

Link: https://github.com/rust-lang/rust/pull/120845

TC: This finished a T-compiler FCP, but ehuss nominates this for us on the grounds of policy:

It has been the policy as long as I'm aware that the lang team is responsible for attributes. For example, they recently approved the debugger_visualizer attribute. I would not expect them to have any concerns here, but I think we should keep with the precedent unless there is an explicit decision by them to change it.

We discussed this in the meeting on 2024-03-27 and felt that it certainly wasn't clear-cut that this wasn't a T-lang matter, and wanted to discuss it with the fuller group.

scottmcm also asked:

One question I had here: This is affecting spans in debug info only? It doesn't affect things like the spans that a proc macro would see?

TC: What do we think?

"static_mut_refs: Should the lint cover hidden references?" rust#123060

Link: https://github.com/rust-lang/rust/issues/123060

TC: What we need to discuss is whether, e.g., these cases should trigger an error in Rust 2024:

// edition: 2024

static mut STATIC: &[u8; 3] = &[0, 1, 2];

fn main() { unsafe {
    let _ = STATIC.len();
    let _ = STATIC[0];
    let _ = format!("{:?}", STATIC);
}}

Playground link

What was implemented in #117556 does not trigger an error for these. However, @scottmcm expressed a view that these should be included also:

I think this should be linting about anything that's created enough of a reference to trigger the reference rules if it can trigger UB when someone else holds a &mut to the static, that's worth linting about here.

And later added:

I think that, if anything, the invisible ones are more important to lint(/break).

At least if someone is doing &STATIC they have a chance at spotting it, but STATIC.frobble() taking a secret reference makes it way harder to manually check.

We discussed this in the 2024-03-27 meeting, and scottmcm proposed FCP merge.

TC: What do we think?

"regression: constants cannot refer to interior mutable data" rust#123281

Link: https://github.com/rust-lang/rust/issues/123281

TC: We unanimously FCPed a decision to accept a stable-to-nightly regression as a consequence of one of our earlier decisions:

https://github.com/rust-lang/rust/issues/121250

The net result looks like this:

[INFO] [stdout] error[E0492]: constants cannot refer to interior mutable data
[INFO] [stdout]    --> src/lib.rs:583:43
[INFO] [stdout]     |
[INFO] [stdout] 583 |     const VTABLE: &'static Self::VTable = &(*Lhs::VTABLE, *Rhs::VTABLE);
[INFO] [stdout]     |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
[INFO] [stdout]

The current issue was nominated for us by apiraino who wants an opinion from us about whether this should be included in the release notes.

"Should dead code in const be monomorphized?" rust#122301

Link: https://github.com/rust-lang/rust/issues/122301

TC: The8472 asks whether this code, which compiles today, can be relied upon:

const fn panic<T>() {
    struct W<T>(T);
    impl<T> W<T> {
        const C: () = panic!();
    }
    W::<T>::C
}

struct Invoke<T, const N: usize>(T);

impl<T, const N: usize> Invoke<T, N> {
    const C: () = match N {
        0 => (),
        // Not called for `N == 0`, so not monomorphized.
        _ => panic::<T>(),
    };
}

fn main() {
    let _x = Invoke::<(), 0>::C;
}

The8472 notes that this is a useful property and that there are use cases for this in the compiler and the standard library, at least unless or until we adopt something like const if:

https://github.com/rust-lang/rfcs/issues/3582

RalfJ has pointed out to The8472 that the current behavior might not be intentional and notes:

It's not opt-dependent, but it's also unclear how we want to resolve the opt-dependent issue. Some proposals involve also walking all items "mentioned" in a const. That would be in direct conflict with your goal here I think. To be clear I think that's a weakness of those proposals. But if that turns out to be the only viable strategy then we'll have to decide what we want more: using const tricks to control what gets monomorphized, or not having optimization-dependent errors.

One crucial part of this construction is that everything involved is generic. If somewhere in the two "branches" you end up calling a monomorphic function, then that may have its constants evaluated even if it is in the "dead" branch or it may not, it depends on which functions are deemed cross-crate-inlinable. That's basically what #122814 is about.

TC: The question to us is whether we want to guarantee this behavior. What do we think?

"Should Rust still ignore SIGPIPE by default?" rust#62569

Link: https://github.com/rust-lang/rust/issues/62569

TC: Prior to main() being executed, the Rust startup code makes a syscall to change the handling of SIGPIPE. Many believe that this is wrong thing for a low-level language like Rust to do, because 1) it makes it impossible to recover what the original value was, and 2) means things like seccomp filters must be adjusted for this.

It's also just, in a practical sense, wrong for most CLI applications.

This seems to have been added back when Rust had green threads and then forgotten about. But it's been an ongoing footgun.

Making a celebrity appearance, Rich Felker, the author of MUSL libc, notes:

As long as Rust is changing signal dispositions inside init code in a way that the application cannot suppress or undo, it is fundamentally unusable to implement standard unix utilities that run child processes or anything that needs to preserve the signal dispositions it was invoked with and pass them on to children. Changing inheritable process state behind the application's back is just unbelievably bad behavior and does not belong in a language runtime for a serious language

As an example, if you implement find in Rust, the -exec option will invoke its commands with SIGPIPE set to SIG_IGN, so that they will not properly terminate on broken pipe. But if you just made it set SIGPIPE to SIG_DFL before invoking the commands, now it would be broken in the case where the invoking user intentionally set SIGPIPE to SIG_IGN so that the commands would not die on broken pipe.

There was discussion in 2019 about fixing this over an edition, but nothing came of it.

Are we interested in fixing it over this one?

Strawman (horrible) proposal: We could stop making this pre-main syscall in Rust 2024 and have cargo fix insert this syscall at the start of every main function.

(In partial defense of the strawman, it gets us directly to the arguably best end result while having an automatic semantics-preserving edition migration and it avoids the concerns about lang/libs coupling that Mara raised.)

"Stabilize min_exhaustive_patterns" rust#122792

Link: https://github.com/rust-lang/rust/pull/122792

TC: Nadri proposes that we stabilize min_exhaustive_patterns:

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:

enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}

This is a subset of the long-unstable exhaustive_patterns feature. That feature is blocked because omitting empty patterns is tricky when not matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.

enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

TC: Further details are here:

https://github.com/rust-lang/rust/pull/122792#issue-2198466801

We discussed this in the 2024-03-27 meeting, and scottmcm proposed FCP merge while leaving a question about the handling of empty structs to future work.

TC: What do we think?

Nadri (adding a quick comment before the meeting): it's not all structs, the only undecided point is whether a #[non_exhaustive] struct can be considered empty.

Nadri: and the only reason this is undecided is that I don't know if other parts of the language make use of the emptiness of types. If this was only about exhaustiveness it would be an easy decision.

"RFC: New range types for Edition 2024" rfcs#3550

Link: https://github.com/rust-lang/rfcs/pull/3550

TC: After we last discussed this on 2024-02-07, we made a number of requests for further analysis and data collection. @pitaj has now delivered on that.

The design document with this data and analysis is here:

https://hackmd.io/@uhs6rVdLTSS0gnie4q0fqA/ryjYJW2pa

And the raw data he extracted from crater is here:

https://github.com/rust-lang/lang-team/files/14659048/data.zip

Let's nominate so that we discuss. Since there is a design document ready and this may call for a design meeting, we've also filed an issue for that here:

https://github.com/rust-lang/lang-team/issues/259

We discussed this (without review of the document) in the meeting on 2024-03-27. The feeling was that it may be difficult for lang to resolve the concern raised directly, though lang may be within its discretion to relax the constraints.

TC: How do we want to approach this?

"Tracking issue for RFC 2102, "Unnamed fields of struct and union type"" rust#49804

Link: https://github.com/rust-lang/rust/issues/49804

TC: fmease nominates this for us:

Nominating Ralf's comment for T-lang discussion. Context for T-lang: There's currently active compiler dev going on to implement this feature (several merged and open PRs by multiple contributors). I don't want them to continue working on it if it gets thrown out in the end.

TC: That comment from RalfJ is:

Unresolved question: what should derive macros do here? This applies both to the built-in ones and user-defined ones. It seems like they all need major overhaul to support types like this. And it is pretty inevitable that people will ask for derive to be supported on these types, even if the MVP does not support them.

OTOH I assume many of them don't support unions to begin with, and these unnamed fields only really make sense when there are unions involved I think?

The RFC also explicitly lists anonymous types as a rejected alternative, and yet the implementation that recently began for this RFC does introduce anonymous ADTs to the compiler. Though maybe if it is impossible to write an expression of these types they are less problematic? That said I assume in the internal compiler IRs such expressions will exist the unnamed fields are getting an internal name and field accesses are desugared to use those names.

It's that kind of issue that makes me think that adding a new form of unnamed types to Rust (on top of closures/coroutines) is a mistake. The RFC was accepted 6 years ago, our approach to language design and evolution changed since then. I think we need to ensure that this is even still something we want to do in this form.

TC: What do we think?

"Tracking issue for function attribute #[coverage]" rust#84605

Link: https://github.com/rust-lang/rust/issues/84605

TC: This is about stabilizing a #[coverage(off)] attribute to exclude items from -Z instrument-coverage.

Josh proposed FCP merge and nominated this for us.

There are two open questions about applying this automatically to nested functions and to inlined functions.

TC: What do we think?

"Stabilize extended_varargs_abi_support" rust#116161

Link: https://github.com/rust-lang/rust/pull/116161

TC: This stabilization was nominated for us, with pnkfelix commenting:

Just to add on to @cjgillot 's comment above: @wesleywiser and I could not remember earlier today whether T-lang wants to own FCP'ing changes like this that are restricted to extending the set of calling-conventions (i.e. the conv in extern "conv" fn foo(...)), which is largely a detail about what platforms one is interoperating with, and not about changing the expressiveness of the Rust language as a whole in the abstract.

(My own gut reaction is that T-compiler is a more natural owner for this than T-lang, but I wasn't certain and so it seems best to let the nomination stand and let the two teams duke it out.)

TC: What do we think about 1) this stabilization, and 2) whether we want to own this?

"Don't make statement nonterminals match pattern nonterminals" rust#120221

Link: https://github.com/rust-lang/rust/pull/120221

TC: CE handed this one to us, since it changes the contract of macro matchers.

Here's the code that does not work today that we would make work:

macro_rules! m {
    ($pat:pat) => {};
    ($stmt:stmt) => {};
}

macro_rules! m2 {
    ($stmt:stmt) => {
        m! { $stmt }
        //~^ ERROR expected pattern
    };
}

m2! { let x = 1 }

This code does not work because we consider :stmt to be a possible :pat even though we then always reject it later in the process. By saying that :stmt cannot be a :pat, we make this code work.

We discussed this in the meeting on 2024-03-27:

CE: Right now the tokens that a macro matcher may begin with is a stable guarantee. We are relaxing the assumption that pattern matchers may begin with statement metavariables ($var whose type is stmt), because when we actually try to parse such a pattern, we are always guaranteed to fail. This only allows more code to compile, and would only break future code if we specifically wanted to begin patterns with statement metavariable.

scottmcm: I agree that it's weird to allow a :stmt in a pattern, so am happy to say we won't. Let's see what others think, since this conversation was in a sparsely-attended triage meeting:

scottmcm: The other thing we explored was what it would take to make this actually work, since you can actually put an :expr into a pattern. But CE argued that we don't actually like that that works, it's just something we're stuck with because people used it before :literal was available, which seems fair.

TC: What do we think?

"Initial support for auto traits with default bounds" rust#120706

Link: https://github.com/rust-lang/rust/pull/120706

TC: This is related to this MCP about a path toward async drop and scoped tasks:

https://github.com/rust-lang/compiler-team/issues/727

TC: petrochenkov gives some background:

So, what are the goals here:

  • We want to have a possibility to add new auto traits that are added to all bound lists by default on the current edition. The examples of such traits could be Leak, Move, SyncDrop or something else, it doesn't matter much right now. The desired behavior is similar to the current Sized trait. Such behavior is required for introducing !Leak or !SyncDrop types in a backward compatible way. (Both Leak and SyncDrop are likely necessary for properly supporting libraries for scoped async tasks and structured concurrency.)
  • It's not clear whether it can be done backward compatibly and without significant perf regressions, but that's exactly what we want to find out. Right now we encounter some cycle errors and exponential blow ups in the trait solver, but there's a chance that they are fixable with the new solver.
  • Then we want to land the change into rustc under an option, so it becomes available in bootstrap compiler. Then we'll be able to do standard library experiments with the aforementioned traits without adding hundreds of #[cfg(not(bootstrap))]s.
  • Based on the experiments, we can come up with some scheme for the next edition, in which such bounds are added more conservatively.
  • Relevant blog posts - https://without.boats/blog/changing-the-rules-of-rust/, https://without.boats/blog/follow-up-to-changing-the-rules-of-rust/ and https://without.boats/blog/generic-trait-methods-and-new-auto-traits/, https://without.boats/blog/the-scoped-task-trilemma/
  • Larger compiler team MCP including this feature - MCP: Low level components for async drop compiler-team#727, it gives some more context

We discussed this in the async WG on 2024-03-25 and commented:

This is interesting work, but there's a lot to review here. We'd be particularly interested in seeing something in the way of a design document here, specifically e.g. with respect to when these bounds are added and when they are not, and how they interact with the ? bounds. Seeing the algorithm spelled out in words and in theory would definitely help us understand this. The best place to put this may be in the rustc-dev-guide.

The question here is whether we want to charter this as an experiment.

"Let's #[expect] some lints: Stabilize lint_reasons (RFC 2383) " rust#120924

Link: https://github.com/rust-lang/rust/pull/120924

TC: Since the last time this was proposed for stabilization, various unresolved questions have now been resolved, so this is being proposed again.

We're talking about this:

#![feature(lint_reasons)]
fn main() {
    #[deny(unused_variables, reason = "unused variables, should be removed")]
    let unused = "How much wood would a woodchuck chuck?";
}
error: unused variable: `unused`
 --> src/main.rs:5:9
  |
5 |     let unused = "How much wood would a woodchuck chuck?";
  |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
  |
  = note: unused variables, should be removed
note: the lint level is defined here
 --> src/main.rs:4:12
  |
4 |     #[deny(unused_variables, reason = "unused variables, should be removed")]
  |            ^^^^^^^^^^^^^^^^

And this:

#![feature(lint_reasons)]
fn main() {
    #[expect(unused_variables, reason = "WIP, I'll use this value later")]
    let message = "How much wood would a woodchuck chuck?";

    #[expect(unused_variables, reason = "is this unused?")]
    let answer = "about 700 pounds";
    println!("A: {answer}")
}
warning: this lint expectation is unfulfilled
 --> src/main.rs:4:14
  |
6 |     #[expect(unused_variables, reason = "is this unused?")]
  |              ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unfulfilled_lint_expectations)]` on by default
  = note: is this unused?

On 2024-03-15, tmandry proposed FCP merge, and nikomatsakis is also +1. This needs one more +1 to go into FCP. What do we think?

"Support ?Trait bounds in supertraits and dyn Trait under a feature gate" rust#121676

Link: https://github.com/rust-lang/rust/pull/121676

TC: This is related to this MCP about a path toward async drop and scoped tasks:

https://github.com/rust-lang/compiler-team/issues/727

TC: petrochenkov gives some background:

Summary:

  • Initial support for auto traits with default bounds #120706 introduces a way to add new auto traits that are appended to all bound lists by default, similarly to existing Sized. Such traits may include Leak, SyncDrop or similar, see Initial support for auto traits with default bounds #120706 (comment) for more detailed motivation.
  • To opt out from bounds added by default the ?Trait syntax is used, but such "maybe" bounds are not supported in some contexts like supertrait lists and dyn Trait + ... lists, because Sized is not added by default in those context.
  • This PR adds a feature for supporting trait Trait1: ?Trait2, dyn Trait1 + ?Trait2 and also multiple maybe bounds in the same list ?Trait1 + ?Trait2, because the new traits need to be added by default in those contexts too, and ?Sized + ?Leak may also make sense.
  • We need this to be available in bootstrap compiler, to make experiments on standard library without adding too many #[cfg(not(bootstrap))]s
  • Larger compiler team MCP including this feature - MCP: Low level components for async drop compiler-team#727, it gives some more context

TC: The question here is whether we want to charter this as an experiment.

"Add simple async drop glue generation" rust#121801

Link: https://github.com/rust-lang/rust/pull/121801

TC: This is related to this MCP about a path toward async drop and scoped tasks:

https://github.com/rust-lang/compiler-team/issues/727

TC: petrochenkov gives some background:

  • Larger compiler team MCP including this feature - MCP: Low level components for async drop compiler-team#727, it gives some more context

  • "Async drop glue" partially implemented in this PR allows to automatically generate an async drop implementation for a structure, enum, or coroutine (or any other complex type) if some of the fields inside it have async drop.

  • This is analogous to generation of a regular drop glue, that drops a type by first calling its surface destructor, and then dropping all its fields in order, but doing all that with futures is more complex.

  • Possible alternatives for this are to require dropping larger structures manually, if they have AsyncDrop fields inside them, or to provide #[derive(AsyncDrop)] for doing this semi-automatically.

    • This approach won't work on coroutines (or closures) containing AsyncDrop types inside them, and coroutines are indeed expected to contain such types.
  • There's always an alternative to reject the async drop feature as a whole, but there's clearly a demand for at least trying to support it and seeing what libraries can do with it. If async drop feature in general goes further in some form (including finally blocks or similar), then the async drop glue generation will likely be a part of it.

We discussed this in the async WG on 2024-03-25 as commented:

This is interesting work, but there's a lot to review here. We'd be particularly interested in seeing something in the way of a design document. There seem to be a lot of new lang items, and we were curious if more of this could be pushed to MIR building. We'd particularly like to see wording in the rustc-dev-guide discussing how this all works, why it's done the way it is, and how we might maintain and extend this going forward.

The question for us is whether we want to charter this as an experiment.

Daria: I've written a post explaining this pull request:

https://github.com/zetanumbers/posts/blob/main/async-drop-design.md

"Elaborate on the invariants for references-to-slices" rust#121965

Link: https://github.com/rust-lang/rust/pull/121965

TC: scottmcm filed this issue and explains:

The length limit on slices is clearly a safety invariant, and I'd like it to also be a validity invariant. With function parameter metadata making progress in LLVM, I'd really like to be able to use it when &[_] is passed as a scalar pair, in particular.

The documentation for references is cagey about what exactly is a validity invariant, so for now just elaborate on the consequences of the existing safety rules on slices the length restriction follows from the size_of_val restriction as a way to help discourage people from trying to violate them.

I also made the existing warning stronger, since I'm fairly sure it's already UB to violate at least the "references must be non-null" rule, rather than it just being that it "might be UB in the future".

Then joboet nominated this for us with:

Given that slice::from_raw_parts already states that "the total size len * mem::size_of::<T>() of the slice must be no larger than isize::MAX" and that its behaviour is undefined otherwise, I'd say that this is entirely uncontroversial. Still, I'd appreciate some team sign-off on this, I think this concerns lang?

RalfJ thinks this should probably be a dual T-lang / T-opsem FCP.

TC: What do we think?

"#![crate_name = EXPR] semantically allows EXPR to be a macro call but otherwise mostly ignores it" rust#122001

Link: https://github.com/rust-lang/rust/issues/122001

TC: In previous stable versions of Rust, #![crate_name = EXPR] worked. That is, within EXPR we expanded and then used macro calls such as concat.

However, due to:

https://github.com/rust-lang/rust/pull/117584

we broke this, and then we shipped it in stable Rust v1.77.

Except, we only half broke it. It doesn't work, but neither is it a hard error. It just quietly ignores the result.

We discussed this in the meeting on 2024-03-27 and agreed this was the worst of all worlds, and so we should at a minimum break it completely, and then we could always later decide to relax the hard error and make it work again by reverting #117584. On that basis, scottmcm proposed FCP merge.

TC: What do we think?

"Assert that the first assert! expression is bool" rust#122661

Link: https://github.com/rust-lang/rust/pull/122661

TC: estebank describes this issue for us:

In the desugaring of assert! in 2024 edition, assign the condition expression to a bool biding in order to provide better type errors when passed the wrong thing.

The span will point only at the expression, and not the whole assert! invocation.

error[E0308]: mismatched types
  --> $DIR/issue-14091.rs:2:13
   |
LL |     assert!(1,1);
   |             ^ expected `bool`, found integer

We no longer mention the expression needing to implement the Not trait.

error[E0308]: mismatched types
  --> $DIR/issue-14091-2.rs:15:13
   |
LL |     assert!(x, x);
   |             ^ expected `bool`, found `BytePos`

In <=2021 edition, we still accept any type that implements Not<Output = bool>.

TC: And pnkfelix nominates this for us:

At the very least, we might need to tie such a change to an edition.

I am not certain whether this decision would be a T-lang matter or a T-libs-api one. I'll nominate for T-lang for now.

(Namely: The question is whether we can start enforcing a rule that the first expression to assert! must be of bool type, which is how the macro is documented, but its current behavior is a little bit more general, as demonstrated in my prior comment)

There is a design space here. E.g. one set of options is:

  1. (stable Rust behavior): in all editions, support arbitrary impl Not<Output=bool> for first parameter to assert!;
  2. in edition >= 2024, support just Deref<Target=bool> for first parameter to assert! (e.g. by expanding to let x: &bool = &$expr;), or
  3. (this PR): in edition >= 2024, support just bool for first parameter to assert!.

(And then there's variations thereof about how to handle editions < 2024, but that's a separate debate IMO.)

TC: What do we think?

"Emit a warning if a match is too complex" rust#122685

Link: https://github.com/rust-lang/rust/pull/122685

TC: Nadri nominates this for us and describes the situation:

Dear T-lang, this PR adds a warning that cannot be silenced, triggered when a match takes a really long time to analyze (in the order of seconds). This is to help users figure out what's taking so long and fix it.

We could make the limit configurable or the warning allowable. I argue that's not necessary because crater showed zero regressions with the current limit, and it's be pretty easy in general to split up a match into smaller matches to avoid blowup.

We're still figuring out the exact limit, but does the team approve in principle?

TC: What do we think?

"Policy for lint expansions" rust#122759

Link: https://github.com/rust-lang/rust/issues/122759

TC: In the call on 2024-03-13, we discussed this issue raised by tmandry:

"Fallout from expansion of redundant import checking"

https://github.com/rust-lang/rust/issues/121708

During the call, the thoughts expressed included:

  • We don't want to create a perverse incentive for people to expand existing lints rather than to create new ones where appropriate just because there's less process for expanding the meaning of an existing lint.
  • It would be good if potentially-disruptive expansions of an existing lint either:
    • Had a machine-applicable fix.
    • Or had a new name.
  • We don't want to require a new lint name for each expansion.
  • We don't want to require a crater run for each change to a lint.
  • There are two ways to prevent disruption worth exploring:
    • Prevent potentially-disruptive changes from hitting master.
    • Respond quickly to early indications of disruption once the changes hit master.
  • Compiler maintainers have a sense of what might be disruptive and are cautious to avoid it. It may be OK to have a policy that is not perfectly measurable.

TC: tmandry volunteered to draft a policy proposal. He's now written up this proposal in this issue.

Background

When a lint is expanded to include many new cases, it adds significant complexity to the rollout of a toolchain to large codebases. Maintainers of these codebases are stuck with the choice of

  1. Disabling the existing lint while the toolchain is updated and new cases are fixed
  2. Fixing cases manually and updating the toolchain immediately

Both of these come with the problem of racing with other developers in a codebase who may land new code which triggers the expanded lint in a new compiler, but does not trigger the lint in an old compiler.

While it would be nice to solve this "raciness" once and for all, there are other considerations at play. Instead, we propose to support these users by either providing them with a new lint name to temporarily opt out of OR a machine-applicable fix which eases the pain of any races which might occur.

Note that this requirement only applies to significant lint expansions as measured by crater.

Policy

When an existing lint is expanded to include many new cases, we must provide either:

  1. A new lint name under the existing group, so that users may opt out of the expansion at least temporarily, or
  2. A MachineApplicable fix for the lint.

Exceptions to this policy may be made via Language Team FCP.

Here, we define "many new cases" as impacting more than 5% of the top-1000 crates on crates.io. This can be measured by counting the number of regressions from a crater run like the one below.

A crater run is not required before landing for every lint expansion. Reviewers should use their best judgment to decide if one is required. However, if a lint expansion lands that violates this requirement, or is strongly suspected to violate this requirement based on other impact, it should be reverted.

Crater command

To measure the impact of a lint as defined by this policy, you can use the following crater command:

@craterbot run name=<name> start=master#<hash1>+rustflags=-D<lint_name> end=master#<hash2>+rustflags=-D<lint_name> crates=top-1000 mode=check-only p=1

See the crater docs for more information.

TC: What do we think?

"Raw Keywords" rfcs#3098

Link: https://github.com/rust-lang/rfcs/pull/3098

TC: We've at various times discussed that we had earlier decided that if we wanted to use a new keyword within an edition, we would write it as k#keyword, and for that reason, we prefer to not speculatively reserve keywords ahead of an edition (except, perhaps, when it's clear we plan to use it in the near future).

TC: Somewhat amusingly, however, we never in fact accepted that RFC. Back in 2021, we accepted scottmcm's proposal to cancel:

We discussed this RFC again in the lang team triage meeting today.

For the short-term goal of the reservation for the edition, we'll be moving forward on #3101 instead. As such, we wanted to leave more time for conversations about this one, and maybe use crater results from 3101 to make design changes,

@rfcbot cancel

Instead we accepted RFC 3101 that reserved ident#foo, ident"foo", ident'f', and ident#123 starting in the 2023 edition.

Reading through the history, here's what I see:

  • What do we want to do about Rust 2015 and Rust 2021? It's a breaking change to add this there. Is this OK? Do we want to do a crater run on this?
  • Would we have the stomach to actually do this? It's one thing to say that if we wanted to use a new keyword within an edition, we'd write k#keyword, but it's another to actually do it in the face of certain criticism about that being e.g. unergonomic. Would we follow through?

TC: What do we think?

"RFC: Drop temporaries in tail expressions before local variables" rfcs#3606

Link: https://github.com/rust-lang/rfcs/pull/3606

TC: This is being broken out of wider work on revisions to temporary lifetime extension.

Currently temporaries in tail position are dropped after locals. This RFC would change them to be dropped before locals in the new edition.

This makes this code work:

fn f() -> usize {
    let c = RefCell::new("..");
    c.borrow().len() // Ok in Rust 2024
}

And it breaks this code:

fn g() -> bool {
    let mut x = None;
    // Make a temporary `RefCell` and put a `Ref` that borrows it in `x`.
    x.replace(RefCell::new(123).borrow()).is_some()
}

What do we think?

"offset: allow zero-byte offset on arbitrary pointers" rust#117329

Link: https://github.com/rust-lang/rust/pull/117329

TC: RalfJ nominates this for us:

Nominating for t-lang discussion. This implements the t-opsem consensus from rust-lang/opsem-team#10, rust-lang/unsafe-code-guidelines#472 to generally allow zero-sized accesses on all pointers. Also see the tracking issue.

  • Zero-sized reads and writes are allowed on all sufficiently aligned pointers, including the null pointer
  • Inbounds-offset-by-zero is allowed on all pointers, including the null pointer
  • offset_from on two pointers is always allowed when they have the same address (but see the caveat below)

This means the following function is safe to be called on any pointer:

fn test_ptr(ptr: *mut ()) { unsafe {
    // Reads and writes.
    let mut val = *ptr;
    *ptr = val;
    ptr.read();
    ptr.write(());
    // Memory access intrinsics.
    // - memcpy (1st and 2nd argument)
    ptr.copy_from_nonoverlapping(&(), 1);
    ptr.copy_to_nonoverlapping(&mut val, 1);
    // - memmove (1st and 2nd argument)
    ptr.copy_from(&(), 1);
    ptr.copy_to(&mut val, 1);
    // - memset
    ptr.write_bytes(0u8, 1);
    // Offset.
    let _ = ptr.offset(0);
    let _ = ptr.offset(1); // this is still 0 bytes
    // Distance.
    let ptr = ptr.cast::<i32>();
    ptr.offset_from(ptr);
} }

Some specific concerns warrant closer scrutiny.

LLVM 16

We currently still support LLVM 16, which does not yet have the patches that make getelementptr inbounds always well-defined for offset 0. The function above thus generates LLVM IR with UB. No known miscompilations arise from that, and my attempt at just removing the inbounds annotation on old versions of LLVM failed (I got segfaults, and Nikic suggested that keeping these attribute around is actually less risky than removing them). If we want to avoid this, we have to wait until support for LLVM 16 can be dropped (which apparently is in May).

Null pointers

t-opsem decided to allow zero-sized reads and writes on null pointers. This is mostly for consistency: we definitely want to allow zero-sized offsets on null pointers (ptr::null::<T>().offset(0)), since this is allowed in C++ (and a proposal is being made to allow it in C) and there's no reason for us to have more UB than C++ here. But if we allow this, and therefore consider the null pointer to have a zero-sized region of "inbounds" memory, then it would be inconsistent to not allow reading from / writing to that region.

offset_from

This operation is somewhat special as it takes two pointers. We do want test_ptr above to be defined on all pointers, so offset_from between two identical pointers without provenance must be allowed. But we also want to achieve this property called "provenance monotonicity", whereby adding arbitrary provenance to any no-provenance pointer must never make the program UB.1 From these two it follows that calling offset_from with two pointers with the same address but arbitrary different provenance must be allowed. This does have some minor downsides. So my proposal (and this goes beyond what t-opsem agreed on) is to define the ptr_offset_from intrinsic to satisfy provenance monotonicity, but to document the user-facing ptr.offset_from(...) as requiring either two pointers without provenance or two pointers with provenance for the same allocation therefore, making the case of provenance mismatch library UB, but not language UB.

Footnotes

  1. This property should hopefully make some intuitive sense, and it is also crucial to justify optimizations that make the program have more provenance than before such as optimizing away provenance-stripping operations. Specifically, *ptr = *ptr where ptr: *mut usize is likely going to be a provenance-stripping operation, and so optimizing away this redundant assignment requires provenance monotonicity.

TC: What do we think?

"#[cold] on match arms" rust#120193

Link: https://github.com/rust-lang/rust/pull/120193

TC: Apparently our unstable likely and unlikely intrinsics don't work. There's a proposal to do some work on fixing that and stabilizing a solution here. The nominated question is whether we want to charter this as an experiment.

"add float semantics RFC" rfcs#3514

Link: https://github.com/rust-lang/rfcs/pull/3514

TC: In addition to documenting the current behavior carefully, this RFC (per RalfJ)

says we should allow float operations in const fn, which is currently not stable. This is a somewhat profound decision since it is the first non-deterministic operation we stably allow in const fn. (We already allow those operations in const/static initializers.)

TC: What do we think? tmandry proposed this for FCP merge back in October 2023.

"Tracking Issue for unicode and escape codes in literals" rust#116907

Link: https://github.com/rust-lang/rust/issues/116907

TC: nnethercote has implemented most of RFC 3349 ("Mixed UTF-8 literals") and, based on implementation experience, argues that the remainder of the RFC should not be implemented:

I have a partial implementation of this RFC working locally (EDIT: now at #120286). The RFC proposes five changes to literal syntax. I think three of them are good, and two of them aren't necessary.

TC: What do we think?

"False positives for the new non_local_definitions lint" rust#121746

Link: https://github.com/rust-lang/rust/issues/121746

TC: There is discussion in the issue in which Urgau explains why this is not a false positive, but the reporter still thinks it's misleading, and Josh nominated this for us.

TC: What do we think?

"Proposal: Remove i128/u128 from the improper_ctypes lint" lang-team#255

Link: https://github.com/rust-lang/lang-team/issues/255

TC: Trevor Gross describes the situation:

For a while, Rust's 128-bit integer types have been incompatible with those from C. The original issue is here rust-lang/rust#54341, with some more concise background information at the MCP here rust-lang/compiler-team#683

The current Beta of 1.77 will have rust-lang/rust#116672, which manually sets the alignment of i128 to make it ABI-compliant with any version of LLVM (clang does something similar now). 1.78 will have LLVM18 as the vendored version which fixes the source of this error.

Proposal: now that we are ABI-compliant, do not raise improper_ctypes on our 128-bit integers. I did some testing with abi-cafe and a more isolated https://github.com/tgross35/quick-abi-check during the time https://reviews.llvm.org/D86310 was being worked on, and verified everything lines up. (It would be great to have some fork of abi-cafe in tree, but that is a separate discussion.)

@joshtriplett mentioned that changing this lint needs a lang FCP https://rust-lang.zulipchat.com/#narrow/stream/187780-t-compiler.2Fwg-llvm/topic/LLVM.20alignment.20of.20i128/near/398422037. cc @maurer

Reference change from when I was testing rust-lang/rust@c742908

TC: Josh nominates this for our discussion. What do we think?

"is operator for pattern-matching and binding" rfcs#3573

Link: https://github.com/rust-lang/rfcs/pull/3573

TC: Josh proposes for us that we should accept:

if an_option is Some(x) && x > 3 {
    println!("{x}");
}

And:

func(x is Some(y) && y > 3);

TC: The main topic discussed in the issue thread so far has been the degree to which Rust should have "two ways to do things". Probably the more interesting issue is how the binding and drop scopes for this should work.

TC: In the 2024-02-21 meeting (with limited attendance), we discussed how we should prioritize stabilizing let chains, and tmandry suggested we may want to allow those to settle first.

TC: What do we think, as a gut check?

"Arbitrary self types v2" rfcs#3519

Link: https://github.com/rust-lang/rfcs/pull/3519

TC: We had a design meeting about this on 2024-02-28. The identified next step was to write up a list of the available options. That document is here:

https://hackmd.io/eXfrzrR7T1-3HVh6w5JMqA

The general mood in the meeting tended in the direction of doing the simpler thing for now (supporting only newtype wrappers) and saving space by giving a hard error on resolution ambiguity, but there were various concerns.

TC: This issue also relates to the subject of the 2024-03-06 RfL meeting on KernelArc:

https://hackmd.io/OCz8EfzrRXeogXEDcOrL2w

TC: The feeling in that meeting was that the newtype approach would probably work for RfL along with the derive(SmartPointer) approach that CE had proposed.

TC: I'm writing up a document to bring together the situation and the various views. So I may reach out to you to set up a call to be sure your views and concerns are faithfully represented.

"RFC: Allow symbol re-export in cdylib crate from linked staticlib" rfcs#3556

Link: https://github.com/rust-lang/rfcs/pull/3556

TC: This seems to be about making the following work:

// kind is optional if it's been specified elsewhere, e.g. via the `-l` flag to rustc
#[link(name="ext", kind="static")]
extern {
    #[no_mangle]
    pub fn foo();

    #[no_mangle]
    pub static bar: std::ffi::c_int;
}

There are apparently use cases for this.

What's interesting is that apparently it already does, but we issue a warning that is wrong:

warning: `#[no_mangle]` has no effect on a foreign function
  --> src/lib.rs:21:5
   |
21 |     #[no_mangle]
   |     ^^^^^^^^^^^^ help: remove this attribute
22 |     pub fn foo_rfc3556_pub_with_no_mangle();
   |     ---------------------------------------- foreign function
   |
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: symbol names in extern blocks are not mangled

TC: One of the author's asks of us is that we don't make this into a hard error (e.g. with the new edition).

TC: What do we think?

"Better errors with bad/missing identifiers in MBEs" rust#118939

Link: https://github.com/rust-lang/rust/pull/118939

TC: The idea here seems to be to improve some diagnostics around macro_rules, but this seems to be done by way of reserving the macro_rules token more widely, which is a breaking change. Petrochenkov has objected to it on that basis, given that reserving macro_rules minimally has been the intention since we hope it will one day disappear in favor of macro. What do we think?

"Uplift clippy::invalid_null_ptr_usage lint" rust#119220

Link: https://github.com/rust-lang/rust/pull/119220

TC: Urgau proposes this for us:

This PR aims at uplifting the clippy::invalid_null_ptr_usage lint into rustc, this is similar to the clippy::invalid_utf8_in_unchecked uplift a few months ago, in the sense that those two lints lint on invalid parameter(s), here a null pointer where it is unexpected and UB to pass one.

invalid_null_ptr_usages

(deny-by-default)

The invalid_null_ptr_usages lint checks for invalid usage of null pointers.

Example

// Undefined behavior
unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
// Not Undefined behavior
unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }

Produces:

error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused, consider using a dangling pointer instead
  --> $DIR/invalid_null_ptr_usages.rs:14:23
   |
LL |     let _: &[usize] = std::slice::from_raw_parts(ptr::null(), 0);
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
   |                                                  |
   |                                                  help: use a dangling pointer instead: `core::ptr::NonNull::dangling().as_ptr()`

Explanation

Calling methods who's safety invariants requires non-null pointer with a null pointer is undefined behavior.

The lint use a list of functions to know which functions and arguments to checks, this could be improved in the future with a rustc attribute, or maybe even with a #[diagnostic] attribute.

TC: What do we think?

"Stop skewing inference in ?'s desugaring" rust#122412

Link: https://github.com/rust-lang/rust/pull/122412

TC: Waffle nominates this breaking change for us:

This changes expr?'s desugaring like so (simplified, see code for more info):

// old
match expr {
    Ok(val) => val,
    Err(err) => return Err(err),
}

// new
match expr {
    Ok(val) => val,
    Err(err) => core::convert::absurd(return Err(err)),
}

// core::convert
pub const fn absurd<T>(x: !) -> T { x }

This prevents ! from the return from skewing inference:

// previously: ok (never type spontaneous decay skews inference, `T = ()`)
// with this pr: can't infer the type for `T`
Err(())?;

We discussed this on 2024-03-20. On the one hand, people were hesitant to block incremental progress, but on the other, people were hesitant to add a special case if we could address a more general case. There was, I would say, appetite for taking a bigger bite here, but people were uncertain if there were any bigger bites that were feasible other than those discussed to support the never type generally, such as disabling fallback to ().

In terms of next steps, we wanted to see an answer about the pros and cons of doing this for return generally, which @WaffleLapkin has now answered:

it made me wonder whether it would be feasible to change return in general to be a free type variable instead of !?

@scottmcm I'm not sure. I don't think it's unfeasible, but it sure is harder than this.

The issues are:

  • Need to add fallback for those type variables too, so that return; works
  • { return; } (which is currently ! even though there is ;) needs to be special cased in a different way
  • Will break strictly more things

I'm not sure if this is a good idea or not. It's kinda weird.

and we wanted to see the results of the crater run that we know that @WaffleLapkin is working to make happen.

When taking this back up, in addition to those details, we wanted to specifically consider how this incremental step may be addressing known footguns with unsafe code such as that in:

https://github.com/rust-lang/rust/issues/51125

TC: What do we think?

"panic in a no-unwind function leads to not dropping local variables" rust#123231

Link: https://github.com/rust-lang/rust/issues/123231

TC: RalfJ nominates this for us. Consider this code:

#![feature(c_unwind)]

struct Noise;
impl Drop for Noise {
    fn drop(&mut self) {
        eprintln!("Noisy Drop");
    }
}

extern "C" fn test() {
    let _val = Noise;
    panic!("heyho");
}

fn main() {
    test();
}

It doesn't print anything. Should it?

"Lang discussion: Item-level const {} blocks, and const { assert!(...) }" lang-team#251

Link: https://github.com/rust-lang/lang-team/issues/251

TC: This issue was raised due to discussion in a T-libs-api call. Josh gives the context:

In discussion of rust-lang/libs-team#325 (a proposal for a compile-time assert macro), the idea came up to allow const {} blocks at item level, and then have people use const { assert!(...) }.

@rust-lang/libs-api would like some guidance from @rust-lang/lang about whether lang is open to toplevel const { ... } blocks like this, which would influence whether we want to add a compile-time assert macro, as well as what we want to call it (e.g. static_assert! vs const_assert! vs some other name).

Filing this issue to discuss in a lang meeting. This issue is not seeking any hard commitment to add such a construct, just doing a temperature check.

CAD97 noted:

To ensure that it's noted: if both item and expression const blocks are valid in the same position (i.e. in statement position), a rule to disambiguate would be needed (like for statement versus expression if-else). IMO it would be quite unfortunate for item-level const blocks to be evaluated pre-mono if that same const block but statement-level would be evaluated post-mono.

Additionally: since const { assert!(...) } is post-mono (due to using the generic context), it's potentially desirable to push people towards using const _: () = assert!(...); (which is pre-mono) whenever possible (not capturing generics).

TC: What do we think?

"unsafe attributes" rfcs#3325

Link: https://github.com/rust-lang/rfcs/pull/3325

TC: On 2024-03-25, Josh resolved the concern he had raised regarding the syntax, so this is now entering FCP.

Unless someone raises a new concern on syntax, the syntax will be:

  • #[unsafe(attr)] ("unsafe parens")

There was a poll that showed strong support for this syntax:

https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Unsafe.20attribute.20syntax

"RFC: Syntax for embedding cargo-script manifests" rfcs#3503

Link: https://github.com/rust-lang/rfcs/pull/3503

TC: This has been changed to use --- syntax with an optional infostring (that is arbitrary except for forbidding whitespace and commas).

TC: tmandry, Josh, and I are +1. What do we think?

".await does not perform autoref or autoderef" rust#111546

Link: https://github.com/rust-lang/rust/issues/111546

TC: This was nominated for T-lang by WG-async on 2023-08-29. tmandry said:

We discussed this in a recent wg-async meeting (notes). The consensus was that we thought the change was well-motivated. At the same time, we want to be cautious about introducing problems (namely backwards compatibility).

There should probably be a crater run of this change, and we should also work through any problematic interactions that could be caused by this change. (@rust-lang/types should probably weigh in.)

The main motivation for the change is the analogy to .method(), as well as to wanting async and sync to feel similarly convenient in most cases.

Note that there is another analogy that works against this, the analogy to IntoIterator, where the lang-effect form (for _ in foo {}) does not do autoref/autoderef. However, given that this looks very different from foo.await, and taking a reference with that form is significantly more convenient (for x in &foo or for x in foo.iter() vs (&foo).await), it seemed the analogy was stretched pretty thin. So we elected to put more weight on the above two considerations.

That being said, this change would need lang team signoff. You can consider this comment wg-async's official recommendation to the lang team.

(Earlier discussion.)

TC: However, it's been a long time since this was nominated, and we may want to recheck the WG-async consensus here. CE, who was not on WG-async at the time of this nomination, has particular concerns (from meeting minutes):

CE: I'm not particularly compelled by this, especially due to the implicit into_future that takes self by value. And there's some danger of this complicating inference.

https://github.com/rust-lang/rust/pull/111773

CE: The inference danger I predicted did show up in that crater run.

https://github.com/rust-lang/rust/pull/111773#issuecomment-1555357572

CE: The least we need to do here is to fully analyze this crater run (much more than has been done to date).

CE: We'd need to do this over an edition, and then we'd have two implementations of the .await operator.

CE: Postfix macros can't autoderef. It doesn't make sense for postfix match either. Match already has match ergonomics.

TC: Consistency with postfix macros does seem compelling.

CE: Indeed, it is essentially a postfix macro.

TC: eholk, who was part of the original consensus for this, found the point about into_future taking self by value compelling in later discussion.

TC: What do we think?

"Add lint against function pointer comparisons" rust#118833

Link: https://github.com/rust-lang/rust/pull/118833

TC: In the 2024-01-03 call, we developed a tentative consensus to lint against direct function pointer comparison and to push people toward using ptr::fn_addr_eq. We decided to ask T-libs-api to add this. There's now an open proposal for that here:

https://github.com/rust-lang/libs-team/issues/323

One question that has come up is whether we would expect this to work like ptr::addr_eq and have separate generic parameters, e.g.:

/// Compares the *addresses* of the two pointers for equality,
/// ignoring any metadata in fat pointers.
///
/// If the arguments are thin pointers of the same type,
/// then this is the same as [`eq`].
pub fn addr_eq<T: ?Sized, U: ?Sized>(p: *const T, q: *const U) -> bool { .. }

Or whether we would prefer that fn_addr_eq enforced type equality of the function pointers. Since we're the ones asking for this, we probably want to develop a consensus here. We discussed this in the call on 2024-01-10, then we opened a Zulip thread:

https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Signature.20of.20.60ptr.3A.3Afn_addr_eq.60

TC: On this subject, scottmcm raised this point, with which pnkfelix seemed to concur:

I do feel like if I saw code that had fn1.addr() == fn2.addr() (if FnPtr were stabilized), I'd write a comment saying "isn't that what fn_addr_eq is for?"

If the answer ends up being "no, actually, because I have different types", that feels unfortunate even if it's rare.

(Like how addr_eq(a, b) is nice even if with strict provenance I could write a.addr() == b.addr() anyway.)

TC: scottmcm also asserted confidence that allowing mixed-type pointer comparisons is correct for ptr::addr_eq since comparing the addresses of *const T, *const [T; N], and *const [T] are all reasonable. I pointed out that, if that's reasonable, then ptr::fn_addr_eq is the higher-ranked version of that, since for the same use cases, it could be reasonable to compare function pointers that return those three different things or accept them as arguments.

TC: Adding to that, scottmcm noted that comparing addresses despite lifetime differences is also compelling, e.g. comparing fn(Box<T>) -> &'static mut T with for<'a> fn(Box<T>) -> &'a mut T.

TC: Other alternatives we considered were not stabilizing ptr::fn_addr_eq at all and instead stabilizing FnPtr so people could write ptr::addr_eq(fn1.addr(), fn2.addr()), or expecting that people would write instead fn1 as *const () == fn2 as *const ().

TC: Recently CAD97 raised an interesting alternative:

From the precedent of ptr::eq and ptr::addr_eq, I'd expect a "ptr::fn_eq" to have one generic type and a "ptr::fn_addr_eq" to have two. Even if ptr::fn_eq's implementation is just an address comparison, it still serves as a documentation point to call out the potential pitfalls with comparing function pointers.

TC: What do we think?


TC: Separately, on the 2024-01-10 call, we discussed some interest use cases for function pointer comparison, especially when it's indirected through PartialEq. We had earlier said we didn't want to lint when such comparisons were indirected through generics, but we did address the non-generic case of simply composing such comparisons.

One example of how this is used is in the standard library, in Waker::will_wake:

https://doc.rust-lang.org/core/task/struct.Waker.html#method.will_wake

It's comparing multiple function pointers via a #[derive(PartialEq)] on the RawWakerVTable.

We decided on 2024-01-01 that this case was interesting and we wanted to think about it further. We opened a discussion thread about this:

https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Function.20pointer.20comparison.20and.20.60PartialEq.60

Since then, another interesting use case in the standard library was raised, in the formatting machinery:

https://doc.rust-lang.org/src/core/fmt/rt.rs.html

What do we think about these, and would we lint on derived PartialEq cases like these or no?

"Implement lint against unexpected unary precedence" rust#121364

Link: https://github.com/rust-lang/rust/pull/121364

TC: The proposal is to lint against:

-2.pow(2); // Equals -4.

These would instead be written:

-(2.pow(2)); // Equals -4.

TC: This is a subset of:

https://github.com/rust-lang/rust/pull/117161

which is also nominated. Whereas the #117161 proposal is to lint on both binary op and unary op cases, this proposal is to lint only on unary op cases. The proposal for this subset came out a discussion with scottmcm.

TC: What do we think?

"Uplift clippy::precedence lint" rust#117161

Link: https://github.com/rust-lang/rust/pull/117161

TC: The proposal is to lint against:

-2.pow(2); // Equals -4.
1 << 2 + 3; // Equals 32.

These would instead be written:

-(2.pow(2)); // Equals -4.
1 << (2 + 3); // Equals 32.

Prompts for discussion:

  • Is this an appropriate lint for rustc?
  • How do other languages handle precedence here?
  • Is minus special enough to treat differently than other unary operators (e.g. !, *, &)?

"types team / lang team interaction" rust#116557

Link: https://github.com/rust-lang/rust/issues/116557

TC: nikomatsakis nominated this:

We had some discussion about types/lang team interaction. We concluded a few things:

  • Pinging the team like @rust-lang/lang is not an effective way to get attention. Nomination is the only official way to get attention.
  • It's ok to nominate things in an "advisory" capacity but not block (e.g., landing a PR), particularly as most any action can ultimately be reversed. But right now, triagebot doesn't track closed issues, so that's a bit risky.

Action items:

  • We should fix triagebot to track closed issues.

TC: What do we think?

"Implement PartialOrd and Ord for Discriminant" rust#106418

Link: https://github.com/rust-lang/rust/pull/106418

TC: We discussed this last in the meeting on 2024-03-13. scottmcm has now raised on concern on the issue and is planning to make a counter-proposal:

I remain concerned about exposing this with no opt-out on an unrestricted generic type @rfcbot concern overly-broad

I'm committing to making an alternative proposal because I shouldn't block without one. Please hold my feet to the fire if that's no up in a week.

Basically, I have an idea for how we might be able to do this, from #106418 (comment)

  1. Expose the variant ordering privately, only accessible by the type owner/module.

Solution 2. is obviously more desirable, but AFAIK Rust can't do that and there is no proposal to add a feature like that.

https://github.com/rust-lang/rust/pull/106418#issuecomment-1994833151

"Fallout from expansion of redundant import checking" rust#121708

Link: https://github.com/rust-lang/rust/issues/121708

TC: We discussed this in the meeting on 2024-03-13. The feelings expressed included:

  • We don't want to create a perverse incentive for people to expand existing lints rather than to create new ones where appropriate just because there's less process for expanding the meaning of an existing lint.
  • It would be good if potentially-disruptive expansions of an existing lint either:
    • Had a machine-applicable fix.
    • Or had a new name.
  • We don't want to require a new lint name for each expansion.
  • We don't want to require a crater run for each change to a lint.
  • There are two ways to prevent disruption worth exploring:
    • Prevent potentially-disruptive changes from hitting master.
    • Respond quickly to early indications of disruption once the changes hit master.
  • Compiler maintainers have a sense of what might be disruptive and are cautious to avoid it. It may be OK to have a policy that is not perfectly measurable.

TC: tmandry volunteered to draft a policy proposal.

Action item review

Pending lang team project proposals

None.

PRs on the lang-team repo

"Add soqb`s design doc to variadics notes" lang-team#236

Link: https://github.com/rust-lang/lang-team/pull/236

"Update auto traits design notes with recent discussion" lang-team#237

Link: https://github.com/rust-lang/lang-team/pull/237

Link: https://github.com/rust-lang/lang-team/pull/258

RFCs waiting to be merged

"Add gen blocks and reserve keyword in Rust 2024" rfcs#3513

Link: https://github.com/rust-lang/rfcs/pull/3513

S-waiting-on-team

"debuginfo: Stabilize -Z debug-macros, -Z collapse-macro-debuginfo and #[collapse_debuginfo]" rust#120845

Link: https://github.com/rust-lang/rust/pull/120845

"Stabilize min_exhaustive_patterns" rust#122792

Link: https://github.com/rust-lang/rust/pull/122792

"privacy: Stabilize lint unnameable_types" rust#120144

Link: https://github.com/rust-lang/rust/pull/120144

"Don't make statement nonterminals match pattern nonterminals" rust#120221

Link: https://github.com/rust-lang/rust/pull/120221

"restrict promotion of const fn calls" rust#121557

Link: https://github.com/rust-lang/rust/pull/121557

"Stabilize count, ignore, index, and length in Rust 1.80" rust#122808

Link: https://github.com/rust-lang/rust/pull/122808

"offset: allow zero-byte offset on arbitrary pointers" rust#117329

Link: https://github.com/rust-lang/rust/pull/117329

"Better errors with bad/missing identifiers in MBEs" rust#118939

Link: https://github.com/rust-lang/rust/pull/118939

"[ptr] Document maximum allocation size" rust#116675

Link: https://github.com/rust-lang/rust/pull/116675

"warn less about non-exhaustive in ffi" rust#116863

Link: https://github.com/rust-lang/rust/pull/116863

"Rename AsyncIterator back to Stream, introduce an AFIT-based AsyncIterator trait" rust#119550

Link: https://github.com/rust-lang/rust/pull/119550

"Allow #[deny] inside #[forbid] as a no-op with a warning" rust#121560

Link: https://github.com/rust-lang/rust/pull/121560

Proposed FCPs

Check your boxes!

"Unsafe Extern Blocks" rfcs#3484

Link: https://github.com/rust-lang/rfcs/pull/3484

"RFC: Reserve unprefixed guarded string literals in Edition 2024" rfcs#3593

Link: https://github.com/rust-lang/rfcs/pull/3593

"Stabilise inline_const" rust#104087

Link: https://github.com/rust-lang/rust/pull/104087

"static_mut_refs: Should the lint cover hidden references?" rust#123060

Link: https://github.com/rust-lang/rust/issues/123060

"Stabilize min_exhaustive_patterns" rust#122792

Link: https://github.com/rust-lang/rust/pull/122792

"RFC: New range types for Edition 2024" rfcs#3550

Link: https://github.com/rust-lang/rfcs/pull/3550

"Tracking issue for function attribute #[coverage]" rust#84605

Link: https://github.com/rust-lang/rust/issues/84605

"Don't make statement nonterminals match pattern nonterminals" rust#120221

Link: https://github.com/rust-lang/rust/pull/120221

"Let's #[expect] some lints: Stabilize lint_reasons (RFC 2383) " rust#120924

Link: https://github.com/rust-lang/rust/pull/120924

"restrict promotion of const fn calls" rust#121557

Link: https://github.com/rust-lang/rust/pull/121557

"#![crate_name = EXPR] semantically allows EXPR to be a macro call but otherwise mostly ignores it" rust#122001

Link: https://github.com/rust-lang/rust/issues/122001

"Stabilize count, ignore, index, and length in Rust 1.80" rust#122808

Link: https://github.com/rust-lang/rust/pull/122808

"add float semantics RFC" rfcs#3514

Link: https://github.com/rust-lang/rfcs/pull/3514

"RFC: Syntax for embedding cargo-script manifests" rfcs#3503

Link: https://github.com/rust-lang/rfcs/pull/3503

"Implement PartialOrd and Ord for Discriminant" rust#106418

Link: https://github.com/rust-lang/rust/pull/106418

"RFC: inherent trait implementation" rfcs#2375

Link: https://github.com/rust-lang/rfcs/pull/2375

"Don't allow unwinding from Drop impls" rfcs#3288

Link: https://github.com/rust-lang/rfcs/pull/3288

"MaybeDangling" rfcs#3336

Link: https://github.com/rust-lang/rfcs/pull/3336

"Add text for the CFG OS Version RFC" rfcs#3379

Link: https://github.com/rust-lang/rfcs/pull/3379

"Stabilize anonymous_lifetime_in_impl_trait" rust#107378

Link: https://github.com/rust-lang/rust/pull/107378

"Tracking Issue for const_cstr_from_ptr" rust#113219

Link: https://github.com/rust-lang/rust/issues/113219

"[ptr] Document maximum allocation size" rust#116675

Link: https://github.com/rust-lang/rust/pull/116675

"Stabilize Wasm relaxed SIMD" rust#117468

Link: https://github.com/rust-lang/rust/pull/117468

"Stabilize associated type position impl Trait (ATPIT)" rust#120700

Link: https://github.com/rust-lang/rust/pull/120700

"Allow #[deny] inside #[forbid] as a no-op with a warning" rust#121560

Link: https://github.com/rust-lang/rust/pull/121560

"regression: let-else syntax restriction (right curly brace not allowed)" rust#121608

Link: https://github.com/rust-lang/rust/issues/121608

Active FCPs

"privacy: Stabilize lint unnameable_types" rust#120144

Link: https://github.com/rust-lang/rust/pull/120144

"unsafe attributes" rfcs#3325

Link: https://github.com/rust-lang/rfcs/pull/3325

"Stabilize Wasm target features that are in phase 4 and 5" rust#117457

Link: https://github.com/rust-lang/rust/pull/117457

P-critical issues

None.