From da61e9cea75df2e9670b693cf5e21557358db38d Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 3 Oct 2026 21:09:32 +0000 Subject: [PATCH 01/13] Add tests for unsoundness involving alias liveness --- .../alias-static-interface-lifetime-151861.rs | 110 +++++++++ ...tatic-interface-lifetime-160979.nll.stderr | 22 ++ ...losure-static-interface-lifetime-160979.rs | 46 ++++ .../alias-liveness/higher-ranked-use-bound.rs | 16 ++ .../borrowck/alias-liveness/higher-ranked.rs | 8 +- .../marker-trait-blanket-151861.nll.stderr | 23 ++ .../marker-trait-blanket-151861.rs | 40 +++ ...rojection-hide-one-bound-153215.nll.stderr | 31 +++ .../projection-hide-one-bound-153215.rs | 88 +++++++ ...on-hide-unrelated-bounds-163886.nll.stderr | 17 ++ ...projection-hide-unrelated-bounds-163886.rs | 59 +++++ ...pit-shorten-through-impl-161038.nll.stderr | 29 +++ .../rpit-shorten-through-impl-161038.rs | 82 +++++++ .../rpit-static-raw-ptr-160979.nll.stderr | 35 +++ .../rpit-static-raw-ptr-160979.rs | 45 ++++ .../alias-liveness/rpit-static-use-bound.rs | 19 ++ .../unrelated-bounds-liveness.rs | 231 ++++++++++++++++++ 17 files changed, 900 insertions(+), 1 deletion(-) create mode 100644 tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs create mode 100644 tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs create mode 100644 tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs create mode 100644 tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.rs create mode 100644 tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.rs create mode 100644 tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs create mode 100644 tests/ui/borrowck/alias-liveness/unrelated-bounds-liveness.rs diff --git a/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs new file mode 100644 index 0000000000000..b1e9a06ea395a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs @@ -0,0 +1,110 @@ +//@ check-pass +//@ known-bug: #151861 + +// This test exhibits UB by creating a dangling reference from a type where the +// lifetime on a trait bound isn't live. This happens because we don't currently +// consider lifetimes in trait bounds on aliases to be live. +// +// Ultimately, the *mechanism* of creating this dangling reference is likely +// unsound, but is only exposed because of the alias-liveness change in 1.75.0. + +trait Produce<'r> { + fn produce(self) -> &'r str; +} + +mod hidden { + pub struct Wrapper(pub std::ptr::NonNull); + + impl<'r> super::Produce<'r> for Wrapper { + fn produce(self) -> &'r str { + unsafe { + // This is almost certainly unsound: `Wrapper` is only ever + // built from a `&'r str` by the `construct` methods below, but + // this impl holds at every `'r`. + // This impl could be easily be called like: + // let d: &'static str = { + // let s = String::from(".."); + // Produce::<'static>::produce(hidden::Wrapper(NonNull::from(&*s))) + // }; + self.0.as_ref() + } + } + } +} + +/// The `'static` bound is left to the user to state as a where-clause. +trait Gat { + type Ty<'r>: Produce<'r>; + fn construct<'r>(r: &'r str) -> Self::Ty<'r>; +} + +/// The `'static` bound is an item bound on the associated type. +trait ItemBound<'r> { + type Ty: 'static + Produce<'r>; + fn construct(r: &'r str) -> Self::Ty; +} + +struct Def; + +impl Gat for Def { + type Ty<'r> = hidden::Wrapper; + fn construct<'r>(r: &'r str) -> Self::Ty<'r> { + hidden::Wrapper(r.into()) + } +} + +impl<'r> ItemBound<'r> for Def { + type Ty = hidden::Wrapper; + fn construct(r: &'r str) -> Self::Ty { + hidden::Wrapper(r.into()) + } +} + +// A generic is used in both cases so that `G::Ty` cannot be normalized. +fn item_bound ItemBound<'r>>() { + let a; + { + let s = String::from("huh"); + a = ::construct(&s); + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.produce(); + println!("{dangling}"); +} + +fn where_clause() +where + for<'r> G::Ty<'r>: 'static, +{ + let a; + { + let s = String::from("huh"); + a = G::construct(&s); + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.produce(); + println!("{dangling}"); +} + +/// The `'static` bound is on an opaque type rather than an associated type: a method returning +/// `impl Produce<'r> + 'static`. +fn rpit_produce<'r>(r: &'r str) -> impl 'static + Produce<'r> { + hidden::Wrapper(r.into()) +} + +fn rpit() { + let a; + { + let s = String::from("huh"); + a = rpit_produce(&s); + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.produce(); + println!("{dangling}"); +} + +fn main() { + item_bound::(); + where_clause::(); + rpit(); +} diff --git a/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr new file mode 100644 index 0000000000000..c46dab96715a8 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr @@ -0,0 +1,22 @@ +error[E0597]: `s` does not live long enough + --> $DIR/closure-static-interface-lifetime-160979.rs:37:42 + | +LL | let s = String::from(""); + | - binding `s` declared here +LL | let cm = ClosureMaker { some_string: &s }; + | ^^ borrowed value does not live long enough +... +LL | let _leaked = leak_it(val); + | ------------ argument requires that `s` is borrowed for `'static` +LL | } + | - `s` dropped here while still borrowed + | +note: requirement that the value outlives `'static` introduced here + --> $DIR/closure-static-interface-lifetime-160979.rs:31:15 + | +LL | fn leak_it(val: T) -> &'static T { + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs new file mode 100644 index 0000000000000..92f16d3fde206 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs @@ -0,0 +1,46 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #160979 + +// Lifetimes in the bounds of an opaque type must be considered live, even +// if they don't appear in the outlives bounds, because they can be "recreated" +// later through the trait's bounds. + +struct Lifetimed<'a> { + #[allow(dead_code)] + some_tuple: &'a (), +} +static LIFETIMED: Lifetimed<'static> = Lifetimed { some_tuple: &() }; + +struct ClosureMaker<'a> { + #[allow(dead_code)] + some_string: &'a str, +} + +impl<'a> ClosureMaker<'a> { + // The closure itself is `'static` (it captures nothing), but its return + // type mentions `'a`. + fn make_closure_simple(&self) -> impl Fn() -> &'a Lifetimed<'a> + 'static { + || &LIFETIMED + } +} + +fn leak_it(val: T) -> &'static T { + Box::leak(Box::new(val)) +} + +fn return_value_with_dangling_lifetime() { + let s = String::from(""); + let cm = ClosureMaker { some_string: &s }; + //[nll]~^ ERROR `s` does not live long enough + let val = cm.make_closure_simple()(); + // requires `&'a Lifetimed<'a>: 'static` + let _leaked = leak_it(val); +} + +fn main() { + return_value_with_dangling_lifetime(); +} diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs b/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs new file mode 100644 index 0000000000000..e638d83357990 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs @@ -0,0 +1,16 @@ +//@ check-pass + +// This is like `higher-ranked-outlives-for-capture`, but represents a case that +// we can accept. Specifically, although `'o` is `use`d here, it is not named in +// any trait or projection bounds. + +trait Outlives<'a>: 'a {} +impl<'a, T: 'a> Outlives<'a> for T {} + +fn test<'o>(v: &'o Vec) -> impl use<'o> + for<'a> Outlives<'a> {} + +fn opaque_doesnt_use_temporary() { + let a = test(&vec![]); +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked.rs b/tests/ui/borrowck/alias-liveness/higher-ranked.rs index 3af125299829b..a06a94c2919f2 100644 --- a/tests/ui/borrowck/alias-liveness/higher-ranked.rs +++ b/tests/ui/borrowck/alias-liveness/higher-ranked.rs @@ -1,4 +1,11 @@ //@ check-pass +//@ known-bug: #151861 + +// We once thought that we could accept this, but alas we cannot. Although in +// this case `Captures<'o>` doesn't do anything, the presence of a trait bound +// with a lifetime can result in unsoundness if the lifetime is not considered +// live. See `marker-trait-blanket-151861` and similar for examples of concrete +// unsoundness. trait Captures<'a> {} impl Captures<'_> for T {} @@ -6,7 +13,6 @@ impl Captures<'_> for T {} trait Outlives<'a>: 'a {} impl<'a, T: 'a> Outlives<'a> for T {} -// Test that we treat `for<'a> Opaque: 'a` as `Opaque: 'static` fn test<'o>(v: &'o Vec) -> impl Captures<'o> + for<'a> Outlives<'a> {} fn opaque_doesnt_use_temporary() { diff --git a/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.nll.stderr b/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.nll.stderr new file mode 100644 index 0000000000000..ee03541e4ec48 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.nll.stderr @@ -0,0 +1,23 @@ +error[E0597]: `s` does not live long enough + --> $DIR/marker-trait-blanket-151861.rs:35:23 + | +LL | let s = String::from("hi"); + | - binding `s` declared here +LL | let r: &str = &s; + | ^^ borrowed value does not live long enough +LL | o = foo(r); +LL | } + | - `s` dropped here while still borrowed +LL | let out: &str = o.get(); +LL | requires_static(out); + | -------------------- argument requires that `s` is borrowed for `'static` + | +note: requirement that the value outlives `'static` introduced here + --> $DIR/marker-trait-blanket-151861.rs:29:23 + | +LL | fn requires_static(val: T) {} + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.rs b/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.rs new file mode 100644 index 0000000000000..b5bb2e6ab2421 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/marker-trait-blanket-151861.rs @@ -0,0 +1,40 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #151861 + +// This shows that it is not sound to treat lifetimes within trait bounds of +// aliases as not live. Lifetimes within the trait bounds can be used to create +// shorter lifetimes than the data within (for example, here, when the inner +// data is `'static`). + +static STATIC: &str = "static"; + +trait Captures<'a> {} +impl Captures<'_> for T {} + +trait Yield<'a> { + fn get(&self) -> &'a str; +} +impl<'a, T: Captures<'a>> Yield<'a> for T { + fn get(&self) -> &'a str { + STATIC + } +} + +fn foo<'a>(_: &'a str) -> impl Sized + Captures<'a> + 'static {} + +fn requires_static(val: T) {} + +fn main() { + let o; + { + let s = String::from("hi"); + let r: &str = &s; //[nll]~ ERROR `s` does not live long enough + o = foo(r); + } + let out: &str = o.get(); + requires_static(out); +} diff --git a/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.nll.stderr b/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.nll.stderr new file mode 100644 index 0000000000000..72353261bb399 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.nll.stderr @@ -0,0 +1,31 @@ +error[E0597]: `x` does not live long enough + --> $DIR/projection-hide-one-bound-153215.rs:49:38 + | +LL | let x = [1, 2, 3]; + | - binding `x` declared here +LL | let short = Rc::new(RefCell::new(&x)); + | ^^ borrowed value does not live long enough +... +LL | let res: &'static [i32; 3] = *long.borrow(); + | ----------------- type annotation requires that `x` is borrowed for `'static` +LL | res +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/projection-hide-one-bound-153215.rs:78:38 + | +LL | let x = [1, 2, 3]; + | - binding `x` declared here +LL | let short = Rc::new(RefCell::new(&x)); + | ^^ borrowed value does not live long enough +... +LL | let res: &'static [i32; 3] = *long.borrow(); + | ----------------- type annotation requires that `x` is borrowed for `'static` +LL | res +LL | } + | - `x` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.rs b/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.rs new file mode 100644 index 0000000000000..49c4e5d469b02 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/projection-hide-one-bound-153215.rs @@ -0,0 +1,88 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ edition: 2024 +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #153215 + +// Data available through an associated type can end up in the hidden type of an +// opaque. We currently don't treat any generics as live, although we *should*. + +#![forbid(unsafe_code)] + +use std::cell::RefCell; +use std::rc::Rc; + +trait Swap: Sized { + fn swap(self, other: Self); +} + +impl Swap for Rc> { + fn swap(self, other: Self) { + >::swap(&self, &other); + } +} + +trait Tr { + type Assoc: Swap; + fn get(self) -> Self::Assoc; +} + +impl<'q> Tr for Rc> { + type Assoc = Rc>; + fn get(self) -> Self::Assoc { + self + } +} + +fn hide_one_bound<'a, S: Tr>(x: S) -> impl Swap + 'a +where + S::Assoc: 'a, +{ + x.get() +} + +fn dangle_one_bound() -> &'static [i32; 3] { + let long = Rc::new(RefCell::new(&[4, 5, 6] as &'static [i32; 3])); + let x = [1, 2, 3]; + let short = Rc::new(RefCell::new(&x)); + //[nll]~^ ERROR `x` does not live long enough + hide_one_bound(long.clone()).swap(hide_one_bound(short)); + let res: &'static [i32; 3] = *long.borrow(); + res +} + + trait TrItemBound<'a> { + type Assoc: Swap + 'a; + fn get(self) -> Self::Assoc; +} + +impl<'q, 'a> TrItemBound<'a> for Rc> +where + 'q: 'a, +{ + type Assoc = Rc>; + fn get(self) -> Self::Assoc { + self + } +} + +fn hide_item_bound<'a, S: TrItemBound<'a>>(x: S) -> impl Swap + 'a { + x.get() +} + +fn dangle_item_bound() -> &'static [i32; 3] { + let long = Rc::new(RefCell::new(&[4, 5, 6] as &'static [i32; 3])); + let x = [1, 2, 3]; + let short = Rc::new(RefCell::new(&x)); + //[nll]~^ ERROR `x` does not live long enough + hide_item_bound(long.clone()).swap(hide_item_bound(short)); + let res: &'static [i32; 3] = *long.borrow(); + res +} + +fn main() { + println!("{:?}", dangle_one_bound()); + println!("{:?}", dangle_item_bound()); +} diff --git a/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.nll.stderr b/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.nll.stderr new file mode 100644 index 0000000000000..026479d511233 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.nll.stderr @@ -0,0 +1,17 @@ +error[E0597]: `x` does not live long enough + --> $DIR/projection-hide-unrelated-bounds-163886.rs:50:38 + | +LL | let x = [1, 2, 3]; + | - binding `x` declared here +LL | let short = Rc::new(RefCell::new(&x)); + | ^^ borrowed value does not live long enough +... +LL | let res: &'static [i32; 3] = *long.borrow(); + | ----------------- type annotation requires that `x` is borrowed for `'static` +LL | res +LL | } + | - `x` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.rs b/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.rs new file mode 100644 index 0000000000000..a837d278bf7d0 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/projection-hide-unrelated-bounds-163886.rs @@ -0,0 +1,59 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ edition: 2024 +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #163886 + +// Similar to `projection-hide-one-bound-153215.rs`, but with two unrelated +// bounds. This used to properly error with Polonius Alpha, but has regressed +// since #156027. + +#![forbid(unsafe_code)] + +use std::cell::RefCell; +use std::rc::Rc; + +trait Swap: Sized { + fn swap(self, other: Self); +} + +impl Swap for Rc> { + fn swap(self, other: Self) { + >::swap(&self, &other); + } +} + +trait Tr { + type Assoc: Swap; + fn get(self) -> Self::Assoc; +} + +impl<'q> Tr for Rc> { + type Assoc = Rc>; + fn get(self) -> Self::Assoc { + self + } +} + +fn hide_unrelated_bounds<'a, 'b, S: Tr>(x: S) -> impl Swap + 'a + 'b +where + S::Assoc: 'a + 'b, +{ + x.get() +} + +fn dangle_unrelated_bounds() -> &'static [i32; 3] { + let long = Rc::new(RefCell::new(&[4, 5, 6] as &'static [i32; 3])); + let x = [1, 2, 3]; + let short = Rc::new(RefCell::new(&x)); + //[nll]~^ ERROR `x` does not live long enough + hide_unrelated_bounds(long.clone()).swap(hide_unrelated_bounds(short)); + let res: &'static [i32; 3] = *long.borrow(); + res +} + +fn main() { + println!("{:?}", dangle_unrelated_bounds()); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr new file mode 100644 index 0000000000000..cc160b516d4d9 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr @@ -0,0 +1,29 @@ +error[E0597]: `*x` does not live long enough + --> $DIR/rpit-shorten-through-impl-161038.rs:63:27 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let r: &Payload = &*x; + | ^^^ borrowed value does not live long enough +LL | wrong = make_producer_alias(r).produce(); +LL | } + | - `*x` dropped here while still borrowed +LL | println!("{wrong}"); + | ----- borrow later used here + +error[E0597]: `*x` does not live long enough + --> $DIR/rpit-shorten-through-impl-161038.rs:73:27 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let r: &Payload = &*x; + | ^^^ borrowed value does not live long enough +LL | wrong = make_producer_ref(r).produce(); +LL | } + | - `*x` dropped here while still borrowed +LL | println!("{wrong}"); + | ----- borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs new file mode 100644 index 0000000000000..1d05fe39fb2ec --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs @@ -0,0 +1,82 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ edition: 2024 +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #161038 + +// Two different variations of #161038: one with a trait with a method taking +// a reference and one taking an impl Trait. Fundamentally, both exploit the +// fact that we don't treat `'a` as live in `Produce<'a>` bound in the return +// type. + +#![forbid(unsafe_code)] + +type Payload = Box; + +trait Produce<'a> { + fn produce(self) -> &'a Payload; +} +impl<'a> Produce<'a> for &'a Payload { + fn produce(self) -> &'a Payload { + self + } +} + +trait OutlivesAlias<'a, 'b> { + fn shorten(x: impl Produce<'a> + 'a) -> impl Produce<'a> + 'b; +} +impl<'a: 'b, 'b> OutlivesAlias<'a, 'b> for () { + fn shorten(x: impl Produce<'a> + 'a) -> impl Produce<'a> + 'b { + x + } +} + +fn make_producer_alias<'a, 'b>(payload: &'a Payload) -> impl Produce<'a> + 'b + use<'a, 'b> +where + (): OutlivesAlias<'a, 'b>, +{ + <() as OutlivesAlias<'a, 'b>>::shorten(payload) +} + +trait OutlivesRef<'a, 'b> { + fn shorten(x: &'a Payload) -> impl Produce<'a> + 'b; +} +impl<'a: 'b, 'b> OutlivesRef<'a, 'b> for () { + fn shorten(x: &'a Payload) -> impl Produce<'a> + 'b { + x + } +} + +fn make_producer_ref<'a, 'b>(payload: &'a Payload) -> impl Produce<'a> + 'b + use<'a, 'b> +where + (): OutlivesRef<'a, 'b>, +{ + <() as OutlivesRef<'a, 'b>>::shorten(payload) +} + +fn through_alias_param() { + let wrong: &Payload; + { + let x: Box = Box::new(Box::new(1)); + let r: &Payload = &*x; //[nll]~ ERROR `*x` does not live long enough + wrong = make_producer_alias(r).produce(); + } + println!("{wrong}"); +} + +fn through_impl_header() { + let wrong: &Payload; + { + let x: Box = Box::new(Box::new(1)); + let r: &Payload = &*x; //[nll]~ ERROR `*x` does not live long enough + wrong = make_producer_ref(r).produce(); + } + println!("{wrong}"); +} + +fn main() { + through_alias_param(); + through_impl_header(); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr new file mode 100644 index 0000000000000..5ab6eabee2b6e --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr @@ -0,0 +1,35 @@ +error[E0597]: `x` does not live long enough + --> $DIR/rpit-static-raw-ptr-160979.rs:40:49 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/rpit-static-raw-ptr-160979.rs:42:10 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- -- borrow of `x` occurs here + | | + | type annotation requires that `x` is borrowed for `'static` +LL | +LL | drop(x); + | ^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let wrong: &'static Payload = make_producer(&x.clone()).produce(); + | ++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0505, E0597. +For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs new file mode 100644 index 0000000000000..bc6cc70548e8f --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs @@ -0,0 +1,45 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] known-bug: #160979 + +// This test exhibits UB by creating a dangling reference from a type where the +// lifetime on a trait bound isn't live. This happens because we don't currently +// consider lifetimes in trait bounds on aliases to be live. +// +// Ultimately, the *mechanism* of creating this dangling reference is likely +// unsound, but is only exposed because the lack of liveness on lifetimes in +// trait bounds on aliases. + + +type Payload = Box; + +trait Produce<'a> { + fn produce(self) -> &'a Payload; +} + +fn make_producer<'a>(payload: &'a Payload) -> impl Produce<'a> + 'static { + struct Producer { + ptr: *const Payload, + } + impl<'a> Produce<'a> for Producer { + fn produce(self) -> &'a Payload { + // This is almost certainly unsound: `Wrapper` is only ever + // built from a `&'a Payload`, but this impl holds at every `'r`. + unsafe { &*self.ptr } + } + } + + Producer { ptr: payload } +} + +fn main() { + let x: Box = Box::new(Box::new(1)); + let wrong: &'static Payload = make_producer(&x).produce(); + //[nll]~^ ERROR `x` does not live long enough + drop(x); + //[nll]~^ ERROR cannot move out of `x` because it is borrowed + println!("{wrong}"); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs b/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs new file mode 100644 index 0000000000000..7cd03e1d75305 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs @@ -0,0 +1,19 @@ +//@ check-pass + +fn foo<'a>(x: &'a mut i32) -> impl Sized + use<'a> + 'static {} + +fn overlapping_mut() { + let i = &mut 1; + let x = foo(i); + let y = foo(i); +} + +fn live_past_borrow() { + let y; + { + let x = &mut 1; + y = foo(x); + } +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/unrelated-bounds-liveness.rs b/tests/ui/borrowck/alias-liveness/unrelated-bounds-liveness.rs new file mode 100644 index 0000000000000..0db5007504ece --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/unrelated-bounds-liveness.rs @@ -0,0 +1,231 @@ +//@ check-pass +//@ known-bug: #163886 +//@ edition: 2024 + +// This test shows that aliases can end up in the hidden type of an impl Trait, +// and that we currently don't properly consider generics live that *should* be. +// This affects both NLL and Polonius Alpha. + +#![forbid(unsafe_code)] + +use std::fmt::Debug; + +trait Tr { + type Assoc: Debug; + fn get(self) -> Self::Assoc; +} +impl<'q> Tr for &'q u8 { + type Assoc = &'q u8; + fn get(self) -> &'q u8 { + self + } +} + +fn make<'a, 'b, T: Tr>(t: T) -> impl Debug + 'a + 'b +where + T::Assoc: 'a + 'b, +{ + t.get() +} + +fn show(t: T, r: &Vec) { + println!("escaped = {t:?}, reused = {r:?}"); +} + +fn base() { + let escaped; + { + let v: Vec = vec![42]; + escaped = make(&v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); +} + +mod item_bounds { + use super::{Debug, show}; + + trait Tr<'a, 'b> { + type Assoc: Debug + 'a + 'b; + fn get(self) -> Self::Assoc; + } + + impl<'a, 'b, 'q: 'a + 'b> Tr<'a, 'b> for &'q u8 { + type Assoc = &'q u8; + fn get(self) -> &'q u8 { + self + } + } + + fn make<'a, 'b, T: Tr<'a, 'b>>(t: T) -> impl Debug + 'a + 'b { + t.get() + } + + pub fn run() { + let escaped; + { + let v: Vec = vec![42]; + escaped = make(&v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } +} + +mod lifetime_arg { + use super::{Debug, show}; + + mod trait_arg { + use super::Debug; + + pub trait Tr<'c> { + type Assoc: Debug; + fn get(self, x: &'c u8) -> Self::Assoc; + } + + impl<'c> Tr<'c> for () { + type Assoc = &'c u8; + fn get(self, x: &'c u8) -> &'c u8 { + x + } + } + + pub fn make<'a, 'b, 'c, T: Tr<'c>>(t: T, x: &'c u8) -> impl Debug + 'a + 'b + where + >::Assoc: 'a + 'b, + { + t.get(x) + } + } + + mod gat_arg { + use super::Debug; + + pub trait Tr { + type Assoc<'z>: Debug; + fn get<'z>(self, x: &'z u8) -> Self::Assoc<'z>; + } + + impl Tr for () { + type Assoc<'z> = &'z u8; + fn get<'z>(self, x: &'z u8) -> &'z u8 { + x + } + } + + pub fn make<'a, 'b, 'z, T: Tr>(t: T, x: &'z u8) -> impl Debug + 'a + 'b + where + T::Assoc<'z>: 'a + 'b, + { + t.get(x) + } + } + + pub fn on_the_trait() { + let escaped; + { + let v: Vec = vec![42]; + escaped = trait_arg::make((), &v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } + + pub fn on_the_gat() { + let escaped; + { + let v: Vec = vec![42]; + escaped = gat_arg::make((), &v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } +} + +mod nested { + use super::{Debug, Tr, show}; + + fn inner<'a, 'b, T: Tr>(t: T) -> impl Debug + 'a + 'b + where + T::Assoc: 'a + 'b, + { + t.get() + } + + fn outer<'a, 'b, T: Tr>(t: T) -> impl Debug + 'a + 'b + where + T::Assoc: 'a + 'b, + { + inner::<'a, 'b, T>(t) + } + + pub fn run() { + let escaped; + { + let v: Vec = vec![42]; + escaped = outer(&v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } +} + +mod rpitit { + use super::{Debug, Tr, show}; + + trait Make<'a, 'b> { + fn make(t: T) -> impl Debug + 'a + 'b + where + T::Assoc: 'a + 'b; + } + + impl<'a, 'b> Make<'a, 'b> for () { + fn make(t: T) -> impl Debug + 'a + 'b + where + T::Assoc: 'a + 'b, + { + t.get() + } + } + + pub fn run() { + let escaped; + { + let v: Vec = vec![42]; + escaped = <() as Make>::make(&v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } +} + +mod three_bounds { + use super::{Debug, Tr, show}; + + fn make<'a, 'b, 'c: 'a, T: Tr>(t: T) -> impl Debug + 'a + 'b + 'c + where + T::Assoc: 'a + 'b + 'c, + { + t.get() + } + + pub fn run() { + let escaped; + { + let v: Vec = vec![42]; + escaped = make(&v[0]); + } + let reused: Vec = vec![7]; + show(escaped, &reused); + } +} + +fn main() { + base(); + item_bounds::run(); + lifetime_arg::on_the_trait(); + lifetime_arg::on_the_gat(); + nested::run(); + rpitit::run(); + three_bounds::run(); +} From 816be680e94850360b5f1aaf136201645f56bc6f Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 25 Oct 2026 10:38:23 +0100 Subject: [PATCH 02/13] Disable recovery when attribute parser shouldn't emit --- compiler/rustc_attr_parsing/src/parser.rs | 9 ++++--- tests/ui/tool-attributes/invalid-tool.rs | 2 -- tests/ui/tool-attributes/invalid-tool.stderr | 26 ++++---------------- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index a5461d9044b96..d5dda24009f34 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -727,10 +727,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { should_emit: ShouldEmit, allow_expr_metavar: AllowExprMetavar, ) -> PResult<'sess, MetaItemListParser> { - let mut parser = Parser::new(psess, tokens, None); - if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { - parser = parser.recovery(recovery); - } + let recovery = match should_emit { + ShouldEmit::ErrorsAndLints { recovery } => recovery, + _ => Recovery::Forbidden, + }; + let mut parser = Parser::new(psess, tokens, None).recovery(recovery); let mut this = MetaItemListParserContext { parser: &mut parser, should_emit, allow_expr_metavar }; diff --git a/tests/ui/tool-attributes/invalid-tool.rs b/tests/ui/tool-attributes/invalid-tool.rs index 51bf9bdf4b043..0b5504c0e205f 100644 --- a/tests/ui/tool-attributes/invalid-tool.rs +++ b/tests/ui/tool-attributes/invalid-tool.rs @@ -4,8 +4,6 @@ //~^ ERROR malformed `register_tool` attribute input #![register_tool(_)] //~^ ERROR expected identifier, found reserved identifier `_` -//~| ERROR expected identifier, found reserved identifier `_` -//~| ERROR expected identifier, found reserved identifier `_` //~| ERROR malformed `register_tool` attribute input // Special path keywords cannot be used. diff --git a/tests/ui/tool-attributes/invalid-tool.stderr b/tests/ui/tool-attributes/invalid-tool.stderr index 5d7742c45d248..b0ee674b4086d 100644 --- a/tests/ui/tool-attributes/invalid-tool.stderr +++ b/tests/ui/tool-attributes/invalid-tool.stderr @@ -1,17 +1,3 @@ -error: expected identifier, found reserved identifier `_` - --> $DIR/invalid-tool.rs:5:18 - | -LL | #![register_tool(_)] - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/invalid-tool.rs:5:18 - | -LL | #![register_tool(_)] - | ^ expected identifier, found reserved identifier - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0565]: malformed `register_tool` attribute input --> $DIR/invalid-tool.rs:3:4 | @@ -31,8 +17,6 @@ error: expected identifier, found reserved identifier `_` | LL | #![register_tool(_)] | ^ expected identifier, found reserved identifier - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0565]: malformed `register_tool` attribute input --> $DIR/invalid-tool.rs:5:4 @@ -49,7 +33,7 @@ LL + #![register_tool(tool1, tool2, ...)] | error[E0565]: malformed `register_tool` attribute input - --> $DIR/invalid-tool.rs:12:4 + --> $DIR/invalid-tool.rs:10:4 | LL | #![register_tool(crate)] | ^^^^^^^^^^^^^^-----^ @@ -63,7 +47,7 @@ LL + #![register_tool(tool1, tool2, ...)] | error[E0565]: malformed `register_tool` attribute input - --> $DIR/invalid-tool.rs:14:4 + --> $DIR/invalid-tool.rs:12:4 | LL | #![register_tool(self)] | ^^^^^^^^^^^^^^----^ @@ -77,7 +61,7 @@ LL + #![register_tool(tool1, tool2, ...)] | error[E0565]: malformed `register_tool` attribute input - --> $DIR/invalid-tool.rs:16:4 + --> $DIR/invalid-tool.rs:14:4 | LL | #![register_tool(Self)] | ^^^^^^^^^^^^^^----^ @@ -91,7 +75,7 @@ LL + #![register_tool(tool1, tool2, ...)] | error[E0565]: malformed `register_tool` attribute input - --> $DIR/invalid-tool.rs:18:4 + --> $DIR/invalid-tool.rs:16:4 | LL | #![register_tool(super)] | ^^^^^^^^^^^^^^-----^ @@ -104,6 +88,6 @@ LL - #![register_tool(super)] LL + #![register_tool(tool1, tool2, ...)] | -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0565`. From e66afcdc3028bb2f5a0cd1063a115360d2709d8d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 15 Oct 2026 17:33:07 +1100 Subject: [PATCH 03/13] Use a raw pointer for ownership of `LLVMBuilderRef` This avoids the unsoundness-hazard of using `&'ll mut` to represent an owning pointer that typically does not live for as long as its enclosing context. --- compiler/rustc_codegen_llvm/src/builder.rs | 190 ++++++++++-------- .../src/builder/autodiff.rs | 2 +- .../src/builder/gpu_offload.rs | 8 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 6 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +- 6 files changed, 114 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 22b6e65e7973c..930463d251d7d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1,6 +1,6 @@ use std::borrow::{Borrow, Cow}; -use std::iter; use std::ops::Deref; +use std::{iter, ptr}; use rustc_ast::expand::typetree::FncTree; pub(crate) mod autodiff; @@ -42,7 +42,8 @@ use crate::type_of::LayoutLlvmExt; #[must_use] pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { - pub llbuilder: &'ll mut llvm::Builder<'ll>, + /// Owning pointer that corresponds to an `LLVMBuilderRef`. Disposed on drop. + llbuilder_raw: ptr::NonNull>, pub cx: &'a GenericCx<'ll, CX>, pub span: rustc_span::Span, } @@ -52,9 +53,7 @@ pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, FullCx<'ll, 'tc impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _)); - } + unsafe { llvm::LLVMDisposeBuilder(self.llbuilder_raw) }; } } @@ -77,7 +76,7 @@ impl<'a, 'll> SBuilder<'a, 'll> { let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( - self.llbuilder, + self.llbuilder(), llty, llfn, args.as_ptr() as *const &llvm::Value, @@ -94,8 +93,8 @@ impl<'a, 'll> SBuilder<'a, 'll> { impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { fn with_cx(scx: &'a GenericCx<'ll, CX>) -> Self { // Create a fresh builder from the simple context. - let llbuilder = unsafe { llvm::LLVMCreateBuilderForModule(scx.deref().borrow().llmod) }; - GenericBuilder { llbuilder, cx: scx, span: rustc_span::DUMMY_SP } + let llbuilder_raw = unsafe { llvm::LLVMCreateBuilderForModule(scx.deref().borrow().llmod) }; + GenericBuilder { llbuilder_raw, cx: scx, span: rustc_span::DUMMY_SP } } pub(crate) fn append_block( @@ -109,28 +108,36 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { } } + /// Returns a reference to this builder's underlying [`llvm::Builder`], + /// tied to the lifetime of self. + pub(crate) fn llbuilder<'s>(&'s self) -> &'s llvm::Builder<'ll> { + // SAFETY: We own this builder, and the reference lifetime is tied to `&self` + // (not to 'll), so it won't be disposed by our Drop while the ref is live. + unsafe { self.llbuilder_raw.as_ref() } + } + pub(crate) fn trunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildTrunc(self.llbuilder(), val, dest_ty, UNNAMED) } } pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildBitCast(self.llbuilder(), val, dest_ty, UNNAMED) } } pub(crate) fn ret_void(&mut self) { - llvm::LLVMBuildRetVoid(self.llbuilder); + llvm::LLVMBuildRetVoid(self.llbuilder()); } pub(crate) fn ret(&mut self, v: &'ll Value) { unsafe { - llvm::LLVMBuildRet(self.llbuilder, v); + llvm::LLVMBuildRet(self.llbuilder(), v); } } pub(crate) fn build(cx: &'a GenericCx<'ll, CX>, llbb: &'ll BasicBlock) -> Self { let bx = Self::with_cx(cx); unsafe { - llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder(), llbb); } bx } @@ -141,10 +148,10 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { // handle this. pub(crate) fn direct_alloca(&mut self, ty: &'ll Type, align: Align, name: &str) -> &'ll Value { let val = unsafe { - let alloca = llvm::LLVMBuildAlloca(self.llbuilder, ty, UNNAMED); + let alloca = llvm::LLVMBuildAlloca(self.llbuilder(), ty, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); // Cast to default addrspace if necessary - llvm::LLVMBuildPointerCast(self.llbuilder, alloca, self.cx.type_ptr(), UNNAMED) + llvm::LLVMBuildPointerCast(self.llbuilder(), alloca, self.cx.type_ptr(), UNNAMED) }; if name != "" { let name = std::ffi::CString::new(name).unwrap(); @@ -161,7 +168,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { ) -> &'ll Value { unsafe { llvm::LLVMBuildGEPWithNoWrapFlags( - self.llbuilder, + self.llbuilder(), ty, ptr, indices.as_ptr(), @@ -176,7 +183,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { debug!("Store {:?} -> {:?}", val, ptr); assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { - let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let store = llvm::LLVMBuildStore(self.llbuilder(), val, ptr); llvm::LLVMSetAlignment(store, align.bytes() as c_uint); store } @@ -184,7 +191,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = llvm::LLVMBuildLoad2(self.llbuilder(), ty, ptr, UNNAMED); llvm::LLVMSetAlignment(load, align.bytes() as c_uint); load } @@ -268,7 +275,7 @@ macro_rules! math_builder_methods { ($($name:ident($($arg:ident),*) => $llvm_capi:ident),+ $(,)?) => { $(fn $name(&mut self, $($arg: &'ll Value),*) -> &'ll Value { unsafe { - llvm::$llvm_capi(self.llbuilder, $($arg,)* UNNAMED) + llvm::$llvm_capi(self.llbuilder(), $($arg,)* UNNAMED) } })+ } @@ -278,7 +285,7 @@ macro_rules! set_math_builder_methods { ($($name:ident($($arg:ident),*) => ($llvm_capi:ident, $llvm_set_math:ident)),+ $(,)?) => { $(fn $name(&mut self, $($arg: &'ll Value),*) -> &'ll Value { unsafe { - let instr = llvm::$llvm_capi(self.llbuilder, $($arg,)* UNNAMED); + let instr = llvm::$llvm_capi(self.llbuilder(), $($arg,)* UNNAMED); llvm::$llvm_set_math(instr); instr } @@ -292,7 +299,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Self { let bx = Builder::with_cx(cx); unsafe { - llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder(), llbb); } bx } @@ -302,7 +309,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn llbb(&self) -> &'ll BasicBlock { - unsafe { llvm::LLVMGetInsertBlock(self.llbuilder) } + unsafe { llvm::LLVMGetInsertBlock(self.llbuilder()) } } fn set_span(&mut self, span: rustc_span::Span) { @@ -325,24 +332,24 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn ret_void(&mut self) { - llvm::LLVMBuildRetVoid(self.llbuilder); + llvm::LLVMBuildRetVoid(self.llbuilder()); } fn ret(&mut self, v: &'ll Value) { unsafe { - llvm::LLVMBuildRet(self.llbuilder, v); + llvm::LLVMBuildRet(self.llbuilder(), v); } } fn br(&mut self, dest: &'ll BasicBlock) { unsafe { - llvm::LLVMBuildBr(self.llbuilder, dest); + llvm::LLVMBuildBr(self.llbuilder(), dest); } } fn br_with_attrs(&mut self, dest: &'ll BasicBlock, loop_hint_attrs: &[UnrollAttr]) { unsafe { - let val = llvm::LLVMBuildBr(self.llbuilder, dest); + let val = llvm::LLVMBuildBr(self.llbuilder(), dest); let mut nodes = Vec::new(); @@ -389,7 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { else_llbb: &'ll BasicBlock, ) { unsafe { - llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); + llvm::LLVMBuildCondBr(self.llbuilder(), cond, then_llbb, else_llbb); } } @@ -400,7 +407,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { cases: impl ExactSizeIterator, ) { let switch = - unsafe { llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, cases.len() as c_uint) }; + unsafe { llvm::LLVMBuildSwitch(self.llbuilder(), v, else_llbb, cases.len() as c_uint) }; for (on_val, dest) in cases { let on_val = self.const_uint_big(self.val_ty(v), on_val); unsafe { llvm::LLVMAddCase(switch, on_val, dest) } @@ -435,7 +442,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { md.push(weight(else_is_cold)); let switch = - unsafe { llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, cases.len() as c_uint) }; + unsafe { llvm::LLVMBuildSwitch(self.llbuilder(), v, else_llbb, cases.len() as c_uint) }; for (on_val, dest, is_cold) in cases { let on_val = self.const_uint_big(self.val_ty(v), on_val); unsafe { llvm::LLVMAddCase(switch, on_val, dest) } @@ -492,7 +499,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let invoke = unsafe { llvm::LLVMBuildInvokeWithOperandBundles( - self.llbuilder, + self.llbuilder(), llty, llfn, args.as_ptr(), @@ -512,7 +519,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn unreachable(&mut self) { unsafe { - llvm::LLVMBuildUnreachable(self.llbuilder); + llvm::LLVMBuildUnreachable(self.llbuilder()); } } @@ -550,7 +557,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn unchecked_suadd(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { unsafe { - let add = llvm::LLVMBuildAdd(self.llbuilder, a, b, UNNAMED); + let add = llvm::LLVMBuildAdd(self.llbuilder(), a, b, UNNAMED); if llvm::LLVMIsAInstruction(add).is_some() { llvm::LLVMSetNUW(add, TRUE); llvm::LLVMSetNSW(add, TRUE); @@ -560,7 +567,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn unchecked_susub(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { unsafe { - let sub = llvm::LLVMBuildSub(self.llbuilder, a, b, UNNAMED); + let sub = llvm::LLVMBuildSub(self.llbuilder(), a, b, UNNAMED); if llvm::LLVMIsAInstruction(sub).is_some() { llvm::LLVMSetNUW(sub, TRUE); llvm::LLVMSetNSW(sub, TRUE); @@ -570,7 +577,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn unchecked_sumul(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { unsafe { - let mul = llvm::LLVMBuildMul(self.llbuilder, a, b, UNNAMED); + let mul = llvm::LLVMBuildMul(self.llbuilder(), a, b, UNNAMED); if llvm::LLVMIsAInstruction(mul).is_some() { llvm::LLVMSetNUW(mul, TRUE); llvm::LLVMSetNSW(mul, TRUE); @@ -581,7 +588,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn or_disjoint(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { unsafe { - let or = llvm::LLVMBuildOr(self.llbuilder, a, b, UNNAMED); + let or = llvm::LLVMBuildOr(self.llbuilder(), a, b, UNNAMED); // If a and b are both values, then `or` is a value, rather than // an instruction, so we need to check before setting the flag. @@ -669,10 +676,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); let ty = self.cx().type_array(self.cx().type_i8(), size.bytes()); unsafe { - let alloca = llvm::LLVMBuildAlloca(bx.llbuilder, ty, UNNAMED); + let alloca = llvm::LLVMBuildAlloca(bx.llbuilder(), ty, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); // Cast to default addrspace if necessary - llvm::LLVMBuildPointerCast(bx.llbuilder, alloca, self.cx().type_ptr(), UNNAMED) + llvm::LLVMBuildPointerCast(bx.llbuilder(), alloca, self.cx().type_ptr(), UNNAMED) } } @@ -682,7 +689,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let scalable_vector_ty = layout.llvm_type(self.cx); unsafe { - let alloca = llvm::LLVMBuildAlloca(&bx.llbuilder, scalable_vector_ty, UNNAMED); + let alloca = llvm::LLVMBuildAlloca(bx.llbuilder(), scalable_vector_ty, UNNAMED); llvm::LLVMSetAlignment(alloca, layout.align.abi.bytes() as c_uint); alloca } @@ -690,7 +697,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = llvm::LLVMBuildLoad2(self.llbuilder(), ty, ptr, UNNAMED); let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); llvm::LLVMSetAlignment(load, align.bytes() as c_uint); load @@ -714,7 +721,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { size: Size, ) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = llvm::LLVMBuildLoad2(self.llbuilder(), ty, ptr, UNNAMED); // Set atomic ordering llvm::LLVMSetOrdering(load, AtomicOrdering::from_generic(order)); if volatile { @@ -883,7 +890,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { debug!("Store {:?} -> {:?} ({:?})", val, ptr, flags); assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { - let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let store = llvm::LLVMBuildStore(self.llbuilder(), val, ptr); let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); let align = align.bytes() as c_uint; llvm::LLVMSetAlignment(store, align); @@ -945,7 +952,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { debug!("Store {:?} -> {:?}", val, ptr); assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { - let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let store = llvm::LLVMBuildStore(self.llbuilder(), val, ptr); // Set atomic ordering llvm::LLVMSetOrdering(store, AtomicOrdering::from_generic(order)); if volatile { @@ -959,7 +966,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn gep(&mut self, ty: &'ll Type, ptr: &'ll Value, indices: &[&'ll Value]) -> &'ll Value { unsafe { llvm::LLVMBuildGEPWithNoWrapFlags( - self.llbuilder, + self.llbuilder(), ty, ptr, indices.as_ptr(), @@ -978,7 +985,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) -> &'ll Value { unsafe { llvm::LLVMBuildGEPWithNoWrapFlags( - self.llbuilder, + self.llbuilder(), ty, ptr, indices.as_ptr(), @@ -997,7 +1004,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) -> &'ll Value { unsafe { llvm::LLVMBuildGEPWithNoWrapFlags( - self.llbuilder, + self.llbuilder(), ty, ptr, indices.as_ptr(), @@ -1010,7 +1017,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { /* Casts */ fn trunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildTrunc(self.llbuilder(), val, dest_ty, UNNAMED) } } fn unchecked_utrunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1038,7 +1045,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn sext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildSExt(self.llbuilder(), val, dest_ty, UNNAMED) } } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1078,7 +1085,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } } - unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildFPToUI(self.llbuilder(), val, dest_ty, UNNAMED) } } fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1097,56 +1104,62 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } } - unsafe { llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildFPToSI(self.llbuilder(), val, dest_ty, UNNAMED) } } fn uitofp(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildUIToFP(self.llbuilder(), val, dest_ty, UNNAMED) } } fn sitofp(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildSIToFP(self.llbuilder(), val, dest_ty, UNNAMED) } } fn fptrunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildFPTrunc(self.llbuilder(), val, dest_ty, UNNAMED) } } fn fpext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildFPExt(self.llbuilder(), val, dest_ty, UNNAMED) } } fn ptrtoint(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildPtrToInt(self.llbuilder(), val, dest_ty, UNNAMED) } } fn inttoptr(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildIntToPtr(self.llbuilder(), val, dest_ty, UNNAMED) } } fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildBitCast(self.llbuilder(), val, dest_ty, UNNAMED) } } fn intcast(&mut self, val: &'ll Value, dest_ty: &'ll Type, is_signed: bool) -> &'ll Value { unsafe { - llvm::LLVMBuildIntCast2(self.llbuilder, val, dest_ty, is_signed.to_llvm_bool(), UNNAMED) + llvm::LLVMBuildIntCast2( + self.llbuilder(), + val, + dest_ty, + is_signed.to_llvm_bool(), + UNNAMED, + ) } } fn pointercast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildPointerCast(self.llbuilder(), val, dest_ty, UNNAMED) } } /* Comparisons */ fn icmp(&mut self, op: IntPredicate, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { let op = llvm::IntPredicate::from_generic(op); - unsafe { llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, UNNAMED) } + unsafe { llvm::LLVMBuildICmp(self.llbuilder(), op as c_uint, lhs, rhs, UNNAMED) } } fn fcmp(&mut self, op: RealPredicate, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { let op = llvm::RealPredicate::from_generic(op); - unsafe { llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, UNNAMED) } + unsafe { llvm::LLVMBuildFCmp(self.llbuilder(), op as c_uint, lhs, rhs, UNNAMED) } } fn three_way_compare( @@ -1177,7 +1190,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let is_volatile = flags.contains(MemFlags::VOLATILE); let memcpy = unsafe { llvm::LLVMRustBuildMemCpy( - self.llbuilder, + self.llbuilder(), dst, dst_align.bytes() as c_uint, src, @@ -1211,7 +1224,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let is_volatile = flags.contains(MemFlags::VOLATILE); unsafe { llvm::LLVMRustBuildMemMove( - self.llbuilder, + self.llbuilder(), dst, dst_align.bytes() as c_uint, src, @@ -1234,7 +1247,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let is_volatile = flags.contains(MemFlags::VOLATILE); unsafe { llvm::LLVMRustBuildMemSet( - self.llbuilder, + self.llbuilder(), ptr, align.bytes() as c_uint, fill_byte, @@ -1245,7 +1258,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn vscale(&mut self, ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMRustBuildVScale(self.llbuilder, ty) } + unsafe { llvm::LLVMRustBuildVScale(self.llbuilder(), ty) } } fn select( @@ -1254,15 +1267,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { then_val: &'ll Value, else_val: &'ll Value, ) -> &'ll Value { - unsafe { llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, UNNAMED) } + unsafe { llvm::LLVMBuildSelect(self.llbuilder(), cond, then_val, else_val, UNNAMED) } } fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } + unsafe { llvm::LLVMBuildVAArg(self.llbuilder(), list, ty, UNNAMED) } } fn extract_element(&mut self, vec: &'ll Value, idx: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, UNNAMED) } + unsafe { llvm::LLVMBuildExtractElement(self.llbuilder(), vec, idx, UNNAMED) } } fn vector_splat(&mut self, num_elts: usize, elt: &'ll Value) -> &'ll Value { @@ -1277,12 +1290,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn extract_value(&mut self, agg_val: &'ll Value, idx: u64) -> &'ll Value { assert_eq!(idx as c_uint as u64, idx); - unsafe { llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, UNNAMED) } + unsafe { llvm::LLVMBuildExtractValue(self.llbuilder(), agg_val, idx as c_uint, UNNAMED) } } fn insert_value(&mut self, agg_val: &'ll Value, elt: &'ll Value, idx: u64) -> &'ll Value { assert_eq!(idx as c_uint as u64, idx); - unsafe { llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, UNNAMED) } + unsafe { + llvm::LLVMBuildInsertValue(self.llbuilder(), agg_val, elt, idx as c_uint, UNNAMED) + } } fn set_personality_fn(&mut self, personality: &'ll Value) { @@ -1312,14 +1327,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { exn = self.insert_value(exn, exn0, 0); exn = self.insert_value(exn, exn1, 1); unsafe { - llvm::LLVMBuildResume(self.llbuilder, exn); + llvm::LLVMBuildResume(self.llbuilder(), exn); } } fn cleanup_pad(&mut self, parent: Option<&'ll Value>, args: &[&'ll Value]) -> Funclet<'ll> { let ret = unsafe { llvm::LLVMBuildCleanupPad( - self.llbuilder, + self.llbuilder(), parent, args.as_ptr(), args.len() as c_uint, @@ -1331,7 +1346,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn cleanup_ret(&mut self, funclet: &Funclet<'ll>, unwind: Option<&'ll BasicBlock>) { unsafe { - llvm::LLVMBuildCleanupRet(self.llbuilder, funclet.cleanuppad(), unwind) + llvm::LLVMBuildCleanupRet(self.llbuilder(), funclet.cleanuppad(), unwind) .expect("LLVM does not have support for cleanupret"); } } @@ -1339,7 +1354,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn catch_pad(&mut self, parent: &'ll Value, args: &[&'ll Value]) -> Funclet<'ll> { let ret = unsafe { llvm::LLVMBuildCatchPad( - self.llbuilder, + self.llbuilder(), parent, args.as_ptr(), args.len() as c_uint, @@ -1357,7 +1372,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) -> &'ll Value { let ret = unsafe { llvm::LLVMBuildCatchSwitch( - self.llbuilder, + self.llbuilder(), parent, unwind, handlers.len() as c_uint, @@ -1389,7 +1404,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) -> (&'ll Value, &'ll Value) { unsafe { let value = llvm::LLVMBuildAtomicCmpXchg( - self.llbuilder, + self.llbuilder(), dst, cmp, src, @@ -1417,7 +1432,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // currently support that (https://github.com/llvm/llvm-project/issues/120837). let mut res = unsafe { llvm::LLVMBuildAtomicRMW( - self.llbuilder, + self.llbuilder(), AtomicRmwBinOp::from_generic(op), dst, src, @@ -1442,7 +1457,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { }; unsafe { llvm::LLVMBuildFence( - self.llbuilder, + self.llbuilder(), AtomicOrdering::from_generic(order), single_threaded.to_llvm_bool(), UNNAMED, @@ -1507,7 +1522,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( - self.llbuilder, + self.llbuilder(), llty, llfn, args.as_ptr() as *const &llvm::Value, @@ -1555,7 +1570,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn zext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty, UNNAMED) } + unsafe { llvm::LLVMBuildZExt(self.llbuilder(), val, dest_ty, UNNAMED) } } fn apply_attrs_to_cleanup_callsite(&mut self, llret: &'ll Value) { @@ -1635,7 +1650,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { fn position_at_start(&mut self, llbb: &'ll BasicBlock) { unsafe { - llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); + llvm::LLVMRustPositionBuilderAtStart(self.llbuilder(), llbb); } } } @@ -1743,7 +1758,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { elt: &'ll Value, idx: &'ll Value, ) -> &'ll Value { - unsafe { llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, UNNAMED) } + unsafe { llvm::LLVMBuildInsertElement(self.llbuilder(), vec, elt, idx, UNNAMED) } } pub(crate) fn shuffle_vector( @@ -1752,7 +1767,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { v2: &'ll Value, mask: &'ll Value, ) -> &'ll Value { - unsafe { llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, UNNAMED) } + unsafe { llvm::LLVMBuildShuffleVector(self.llbuilder(), v1, v2, mask, UNNAMED) } } pub(crate) fn vector_reduce_fadd(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { @@ -1827,7 +1842,8 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { funclet: &Funclet<'ll>, unwind: &'ll BasicBlock, ) -> &'ll Value { - let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) }; + let ret = + unsafe { llvm::LLVMBuildCatchRet(self.llbuilder(), funclet.cleanuppad(), unwind) }; ret.expect("LLVM does not have support for catchret") } @@ -1873,7 +1889,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { } pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } + unsafe { llvm::LLVMBuildVAArg(self.llbuilder(), list, ty, UNNAMED) } } } @@ -1913,7 +1929,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { bbs: &[&'ll BasicBlock], ) -> &'ll Value { assert_eq!(vals.len(), bbs.len()); - let phi = unsafe { llvm::LLVMBuildPhi(self.llbuilder, ty, UNNAMED) }; + let phi = unsafe { llvm::LLVMBuildPhi(self.llbuilder(), ty, UNNAMED) }; unsafe { llvm::LLVMAddIncoming(phi, vals.as_ptr(), bbs.as_ptr(), vals.len() as c_uint); phi @@ -1938,7 +1954,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // personality lives on the parent function anyway. self.set_personality_fn(pers_fn); unsafe { - llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED) + llvm::LLVMBuildLandingPad(self.llbuilder(), ty, None, num_clauses as c_uint, UNNAMED) } } @@ -1979,7 +1995,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let callbr = unsafe { llvm::LLVMBuildCallBr( - self.llbuilder, + self.llbuilder(), llty, llfn, default_dest, diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 3b1e36265b941..58b8cb1124d85 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -193,7 +193,7 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( // Now we multiply `4 * next_outer_arg` to get the stride. let mul = unsafe { llvm::LLVMBuildMul( - builder.llbuilder, + builder.llbuilder(), cx.get_const_int(cx.type_i64(), elem_bytes_size), next_outer_arg, UNNAMED, diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index d3bb8d7c3e6a4..172560dcb2524 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -560,7 +560,7 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( // Step 0) unsafe { - llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn()); + llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder(), builder.llfn()); } let ty = cx.type_array(cx.type_ptr(), num_args); @@ -594,7 +594,7 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( // Step 1) unsafe { - llvm::LLVMPositionBuilderAtEnd(&builder.llbuilder, bb); + llvm::LLVMPositionBuilderAtEnd(builder.llbuilder(), bb); } // Now we allocate once per function param, a copy to be passed to one of our maps. @@ -612,11 +612,11 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( let bb = builder.llbb(); unsafe { - llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder, builder.llfn()); + llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder(), builder.llfn()); } let addr = builder.direct_alloca(cx.type_i64(), Align::EIGHT, "addr"); unsafe { - llvm::LLVMPositionBuilderAtEnd(builder.llbuilder, bb); + llvm::LLVMPositionBuilderAtEnd(builder.llbuilder(), bb); } let cast = builder.bitcast(v, cx.type_ix(num_bits)); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 99a54f1d74a62..eb923316cc649 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -121,7 +121,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { impl<'ll> Builder<'_, 'll, '_> { pub(crate) fn get_dbg_loc(&self) -> Option<&'ll DILocation> { - unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) } + unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder()) } } } @@ -576,13 +576,13 @@ impl<'ll, 'tcx> DebugInfoBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { unsafe { - llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc); + llvm::LLVMSetCurrentDebugLocation2(self.llbuilder(), dbg_loc); } } fn clear_dbg_loc(&mut self) { unsafe { - llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, ptr::null()); + llvm::LLVMSetCurrentDebugLocation2(self.llbuilder(), ptr::null()); } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a08ebbb28fd20..5b21819689afc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -985,7 +985,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let llret = unsafe { llvm::LLVMBuildCallWithOperandBundles( - self.llbuilder, + self.llbuilder(), fn_ty, fn_ptr, llargs.as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b109772eb501b..d172de06a244a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1196,10 +1196,10 @@ unsafe extern "C" { ); // Instruction builders - pub(crate) fn LLVMCreateBuilderForModule(M: &Module) -> &mut Builder<'_>; + pub(crate) fn LLVMCreateBuilderForModule<'ll>(M: &'ll Module) -> ptr::NonNull>; pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); pub(crate) fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; - pub(crate) fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); + pub(crate) fn LLVMDisposeBuilder<'ll>(Builder: ptr::NonNull>); // Metadata pub(crate) fn LLVMSetCurrentDebugLocation2<'a>(Builder: &Builder<'a>, Loc: *const Metadata); From 74346591e8ed4ffe46077740b37a5b9bab6c6044 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 23 Oct 2026 20:59:13 +1100 Subject: [PATCH 04/13] Tweak some SimpleCx lifetimes The SimpleCx type contains references tied to the lifetime of the LLVM context, but the SimpleCx itself has a shorter lifetime. --- compiler/rustc_codegen_llvm/src/allocator.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index ef77abd53527d..3550e62ff6e25 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -106,13 +106,13 @@ pub(crate) unsafe fn codegen( } } -fn create_wrapper_function( +fn create_wrapper_function<'ll>( tcx: TyCtxt<'_>, - cx: &SimpleCx<'_>, + cx: &SimpleCx<'ll>, from_name: &str, to_name: Option<&str>, - args: &[&Type], - output: Option<&Type>, + args: &[&'ll Type], + output: Option<&'ll Type>, no_return: bool, attrs: &CodegenFnAttrs, ) { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 872c022373d45..28332107b172c 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -654,7 +654,7 @@ pub(crate) unsafe fn llvm_optimize<'ll>( None }; - fn handle_offload<'ll>(cx: &'ll SimpleCx<'_>, old_fn: &llvm::Value) { + fn handle_offload<'ll>(cx: &SimpleCx<'ll>, old_fn: &'ll llvm::Value) { let old_fn_ty = cx.get_type_of_global(old_fn); let old_param_types = cx.func_params_types(old_fn_ty); let old_param_count = old_param_types.len(); diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 172560dcb2524..de7b4688f1471 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -475,7 +475,7 @@ fn declare_offload_fn<'ll>( ) } -pub(crate) fn scalar_width<'ll>(cx: &'ll SimpleCx<'_>, ty: &'ll Type) -> u64 { +pub(crate) fn scalar_width<'ll>(cx: &SimpleCx<'ll>, ty: &'ll Type) -> u64 { match cx.type_kind(ty) { TypeKind::Half | TypeKind::Float From 3b72d26931342ee9a5841c712d65cdaa9545197b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 15 Oct 2026 18:14:43 +1100 Subject: [PATCH 05/13] Use an invariant lifetime brand for LLVM contexts This commit changes the type of LLVM context references from `&'ll Context` to a dedicated newtype struct `Context<'ll>` with an invariant lifetime brand. The current usage of `&'ll Context` is not sound, because covariance in `'ll` allows different contexts to be mixed in ways that the underlying bindings are not prepared to handle. (Most of the time, mixing contexts is unlikely to happen by accident, but switching to a more-sound API is good maintenance hygiene for these complex bindings.) --- compiler/rustc_codegen_llvm/src/attributes.rs | 7 +- .../src/back/llvm_backend.rs | 26 ++- compiler/rustc_codegen_llvm/src/back/lto.rs | 8 +- compiler/rustc_codegen_llvm/src/back/write.rs | 22 ++- compiler/rustc_codegen_llvm/src/common.rs | 6 +- compiler/rustc_codegen_llvm/src/context.rs | 8 +- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 21 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 186 +++++++++++------- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 40 ++-- compiler/rustc_codegen_llvm/src/mono_item.rs | 6 +- compiler/rustc_codegen_llvm/src/type_.rs | 6 +- compiler/rustc_codegen_llvm/src/typetree.rs | 8 +- 12 files changed, 205 insertions(+), 139 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 56931aff992b6..bdbd43de07a53 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -212,7 +212,10 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. #[inline] -pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option) -> &Attribute { +pub(crate) fn uwtable_attr<'ll>( + llcx: llvm::Context<'ll>, + use_sync_unwind: Option, +) -> &'ll Attribute { // NOTE: We should determine if we even need async unwind tables, as they // take have more overhead and if we can use sync unwind tables we // probably should. @@ -488,7 +491,7 @@ pub(crate) fn default_optimisation_attrs<'ll>( attrs } -fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { +fn create_alloc_family_attr<'ll>(llcx: llvm::Context<'ll>) -> &'ll llvm::Attribute { llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") } diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 385b785233a75..6a5c50b3b9212 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -442,7 +442,7 @@ impl CodegenBackend for LlvmCodegenBackend { } pub struct ModuleLlvm { - llcx_raw: ptr::NonNull, + llcx_raw: ptr::NonNull, llmod_raw: *const llvm::Module, // This field is `ManuallyDrop` because it is important that the `TargetMachine` @@ -459,12 +459,22 @@ pub struct ModuleLlvm { unsafe impl Send for ModuleLlvm {} unsafe impl Sync for ModuleLlvm {} +/// Converts an unbranded pointer-to-context to a lifetime-branded [`llvm::Context`]. +/// +/// # Safety +/// - The pointer must be a valid LLVM context. +/// - If the context is passed to safe code, its caller-requested lifetime must be distinct +/// from that of any other context, and must not outlive the underlying context. +unsafe fn wrap_llcx<'ll>(llcx_raw: ptr::NonNull) -> llvm::Context<'ll> { + llvm::Context { llcx_raw, _invariant_ll: PhantomData } +} + /// Shared helper to call [`llvm::LLVMContextCreate`] and then configure /// [`llvm::LLVMContextSetDiscardValueNames`]. -fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull { +fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull { unsafe { let llcx_raw = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx_raw.as_ref(), fewer_names.to_llvm_bool()); + llvm::LLVMContextSetDiscardValueNames(wrap_llcx(llcx_raw), fewer_names.to_llvm_bool()); llcx_raw } } @@ -473,7 +483,7 @@ impl ModuleLlvm { pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); - let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); + let llmod_raw = context::create_module(tcx, wrap_llcx(llcx_raw), mod_name); ModuleLlvm { llmod_raw, llcx_raw, @@ -486,7 +496,7 @@ impl ModuleLlvm { pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); - let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); + let llmod_raw = context::create_module(tcx, wrap_llcx(llcx_raw), mod_name); ModuleLlvm { llmod_raw, llcx_raw, @@ -505,7 +515,7 @@ impl ModuleLlvm { ) -> Self { unsafe { let llcx_raw = create_context_and_set_fewer_names(cgcx.fewer_names); - let llmod_raw = back::lto::parse_module(llcx_raw.as_ref(), name, buffer, dcx); + let llmod_raw = back::lto::parse_module(wrap_llcx(llcx_raw), name, buffer, dcx); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); ModuleLlvm { @@ -527,7 +537,7 @@ impl ModuleLlvm { inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, ) -> R { inner_fn(ModuleRefs { - llcx: unsafe { self.llcx_raw.as_ref() }, + llcx: unsafe { wrap_llcx(self.llcx_raw) }, llmod: unsafe { &*self.llmod_raw }, tm: self.tm.raw(), _ph: PhantomData, @@ -547,7 +557,7 @@ impl Drop for ModuleLlvm { /// Bundle of references provided by [`ModuleLlvm::with_module_refs`]. #[derive(Clone, Copy)] pub(crate) struct ModuleRefs<'ll> { - pub(crate) llcx: &'ll llvm::Context, + pub(crate) llcx: llvm::Context<'ll>, pub(crate) llmod: &'ll llvm::Module, pub(crate) tm: &'ll llvm::TargetMachine, /// Make sure `'ll` is invariant, even if no fields happen to require that. diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index c99eb45438353..4ba4ca6e03b89 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -854,14 +854,14 @@ fn module_name_to_str(c_str: &CStr) -> &str { }) } -pub(crate) fn parse_module<'a>( - cx: &'a llvm::Context, +pub(crate) fn parse_module<'ll>( + llcx: llvm::Context<'ll>, name: &CStr, data: &[u8], dcx: DiagCtxtHandle<'_>, -) -> &'a llvm::Module { +) -> &'ll llvm::Module { unsafe { - llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr()) + llvm::LLVMRustParseBitcodeForLTO(llcx, data.as_ptr(), data.len(), name.as_ptr()) .unwrap_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode)) } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 28332107b172c..b5219edf4efa2 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -326,8 +326,8 @@ struct DiagnosticHandlerCallbackData<'a> { shared_emitter: &'a SharedEmitter, } -pub(crate) struct DiagnosticHandlers<'a> { - llcx: &'a llvm::Context, +pub(crate) struct DiagnosticHandlers<'a, 'll> { + llcx: llvm::Context<'ll>, old_handler: Option>, /// Ownership of the box-allocated data that was installed (as a pointer) @@ -337,11 +337,11 @@ pub(crate) struct DiagnosticHandlers<'a> { diagnostic_handler_callback_data: *mut DiagnosticHandlerCallbackData<'a>, } -impl<'a> DiagnosticHandlers<'a> { +impl<'a, 'll> DiagnosticHandlers<'a, 'll> { pub(crate) fn new( cgcx: &'a CodegenContext, shared_emitter: &'a SharedEmitter, - llcx: &'a llvm::Context, + llcx: llvm::Context<'ll>, module: &ModuleCodegen, stage: CodegenDiagnosticsStage, ) -> Self { @@ -396,7 +396,7 @@ impl<'a> DiagnosticHandlers<'a> { } } -impl<'a> Drop for DiagnosticHandlers<'a> { +impl<'a, 'll> Drop for DiagnosticHandlers<'a, 'll> { fn drop(&mut self) { unsafe { llvm::LLVMRustContextSetDiagnosticHandler(self.llcx, self.old_handler); @@ -1205,10 +1205,10 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr { } /// Embed the bitcode of an LLVM module for LTO in the LLVM module itself. -fn embed_bitcode( +fn embed_bitcode<'ll>( cgcx: &CodegenContext, - llcx: &llvm::Context, - llmod: &llvm::Module, + llcx: llvm::Context<'ll>, + llmod: &'ll llvm::Module, bitcode: &[u8], ) { // We're adding custom sections to the output object file, but we definitely @@ -1291,7 +1291,11 @@ fn embed_bitcode( // when using MSVC linker. We do this only for data, as linker can fix up // code references on its own. // See #26591, #27438 -fn create_msvc_imps(cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module) { +fn create_msvc_imps<'ll>( + cgcx: &CodegenContext, + llcx: llvm::Context<'ll>, + llmod: &'ll llvm::Module, +) { if !cgcx.msvc_imps_needed { return; } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 6e8f005be9569..a7fdf9b2d8a76 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -469,12 +469,12 @@ pub(crate) fn val_ty(v: &Value) -> &Type { unsafe { llvm::LLVMTypeOf(v) } } -pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> &'ll Value { +pub(crate) fn bytes_in_context<'ll>(llcx: llvm::Context<'ll>, bytes: &[u8]) -> &'ll Value { unsafe { llvm::LLVMConstStringInContext2(llcx, bytes.as_ptr(), bytes.len(), TRUE) } } pub(crate) fn null_terminate_bytes_in_context<'ll>( - llcx: &'ll llvm::Context, + llcx: llvm::Context<'ll>, bytes: &[u8], ) -> &'ll Value { unsafe { llvm::LLVMConstStringInContext2(llcx, bytes.as_ptr(), bytes.len(), FALSE) } @@ -486,7 +486,7 @@ pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Valu } fn struct_in_context<'ll>( - llcx: &'ll llvm::Context, + llcx: llvm::Context<'ll>, elts: &[&'ll Value], packed: bool, ) -> &'ll Value { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 1a323e550623b..8ef5ce26e1c2d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -48,7 +48,7 @@ use crate::{attributes, common, coverageinfo, debuginfo, llvm_util}; /// moved around more freely. pub(crate) struct SCx<'ll> { pub llmod: &'ll llvm::Module, - pub llcx: &'ll llvm::Context, + pub llcx: llvm::Context<'ll>, pub isize_ty: &'ll Type, } @@ -184,7 +184,7 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { pub(crate) unsafe fn create_module<'ll>( tcx: TyCtxt<'_>, - llcx: &'ll llvm::Context, + llcx: llvm::Context<'ll>, mod_name: &str, ) -> *const llvm::Module { let mod_name_cstr = SmallCStr::new(mod_name); @@ -194,7 +194,7 @@ pub(crate) unsafe fn create_module<'ll>( llmod_raw } -fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: &'ll llvm::Context, llmod: &'ll llvm::Module) { +fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: llvm::Context<'ll>, llmod: &'ll llvm::Module) { let sess = tcx.sess; let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); @@ -881,7 +881,7 @@ impl<'ll> SimpleCx<'ll> { impl<'ll> SimpleCx<'ll> { pub(crate) fn new( llmod: &'ll llvm::Module, - llcx: &'ll llvm::Context, + llcx: llvm::Context<'ll>, pointer_size: Size, ) -> Self { let isize_ty = llvm::LLVMIntTypeInContext(llcx, pointer_size.bits() as c_uint); diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 8f6cc30e1166d..b0641b70d1189 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -3,8 +3,8 @@ use libc::{c_char, c_uchar, c_uint}; use super::MetadataKindId; -use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value}; -use crate::llvm::{Bool, Builder}; +use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; +use crate::llvm::{Bool, Builder, Context}; // TypeTree types pub(crate) type CTypeTreeRef = *mut EnzymeTypeTree; @@ -108,7 +108,8 @@ pub(crate) mod Enzyme_AD { type EnzymeSetCLStringFn = unsafe extern "C" fn(*mut c_void, *const c_char); type EnzymeNewTypeTreeFn = unsafe extern "C" fn() -> CTypeTreeRef; - type EnzymeNewTypeTreeCTFn = unsafe extern "C" fn(CConcreteType, &Context) -> CTypeTreeRef; + type EnzymeNewTypeTreeCTFn = + for<'ll> unsafe extern "C" fn(CConcreteType, Context<'ll>) -> CTypeTreeRef; type EnzymeNewTypeTreeTRFn = unsafe extern "C" fn(CTypeTreeRef) -> CTypeTreeRef; type EnzymeFreeTypeTreeFn = unsafe extern "C" fn(CTypeTreeRef); type EnzymeMergeTypeTreeFn = unsafe extern "C" fn(CTypeTreeRef, CTypeTreeRef) -> bool; @@ -117,7 +118,7 @@ pub(crate) mod Enzyme_AD { type EnzymeTypeTreeShiftIndiciesEqFn = unsafe extern "C" fn(CTypeTreeRef, *const c_char, i64, i64, u64); type EnzymeTypeTreeInsertEqFn = - unsafe extern "C" fn(CTypeTreeRef, *const i64, usize, CConcreteType, &Context); + for<'ll> unsafe extern "C" fn(CTypeTreeRef, *const i64, usize, CConcreteType, Context<'ll>); type EnzymeTypeTreeToStringFn = unsafe extern "C" fn(CTypeTreeRef) -> *const c_char; type EnzymeTypeTreeToStringFreeFn = unsafe extern "C" fn(*const c_char); @@ -236,10 +237,10 @@ pub(crate) mod Enzyme_AD { unsafe { (self.EnzymeNewTypeTree)() } } - pub(crate) fn new_type_tree_ct( + pub(crate) fn new_type_tree_ct<'ll>( &self, t: CConcreteType, - ctx: &Context, + ctx: Context<'ll>, ) -> *mut EnzymeTypeTree { unsafe { (self.EnzymeNewTypeTreeCT)(t, ctx) } } @@ -283,13 +284,13 @@ pub(crate) mod Enzyme_AD { } } - pub(crate) fn tree_insert_eq( + pub(crate) fn tree_insert_eq<'ll>( &self, tree: CTypeTreeRef, indices: *const i64, len: usize, ct: CConcreteType, - ctx: &Context, + ctx: Context<'ll>, ) { unsafe { (self.EnzymeTypeTreeInsertEq)(tree, indices, len, ct, ctx) } } @@ -476,7 +477,7 @@ impl TypeTree { TypeTree { inner } } - pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree { + pub(crate) fn from_type<'ll>(t: CConcreteType, ctx: Context<'ll>) -> TypeTree { let wrapper = EnzymeWrapper::get_instance(); let inner = wrapper.new_type_tree_ct(t, ctx); TypeTree { inner } @@ -510,7 +511,7 @@ impl TypeTree { self } - pub(crate) fn insert(&mut self, indices: &[i64], ct: CConcreteType, ctx: &Context) { + pub(crate) fn insert<'ll>(&mut self, indices: &[i64], ct: CConcreteType, ctx: Context<'ll>) { let wrapper = EnzymeWrapper::get_instance(); wrapper.tree_insert_eq(self.inner, indices.as_ptr(), indices.len(), ct, ctx); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d172de06a244a..7e6c4869f0b26 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -700,9 +700,19 @@ struct InvariantOpaque<'a> { } // Opaque pointer types + +/// Corresponds to `LLVMContextRef`. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub(crate) struct Context<'ll> { + pub(crate) llcx_raw: ptr::NonNull, + pub(crate) _invariant_ll: PhantomData &'ll ()>, +} + unsafe extern "C" { + /// Pointee of the raw pointer that is wrapped by a lifetime-branded [`Context`]. + pub(crate) type ContextRaw; pub(crate) type Module; - pub(crate) type Context; pub(crate) type Type; pub(crate) type Value; pub(crate) type ConstantInt; @@ -887,11 +897,11 @@ pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> unsafe extern "C" { // Create and destroy contexts. - pub(crate) fn LLVMContextCreate() -> ptr::NonNull; - pub(crate) fn LLVMContextDispose(C: ptr::NonNull); - pub(crate) fn LLVMContextSetDiscardValueNames(C: &Context, Discard: Bool); - pub(crate) fn LLVMGetMDKindIDInContext( - C: &Context, + pub(crate) fn LLVMContextCreate() -> ptr::NonNull; + pub(crate) fn LLVMContextDispose(C: ptr::NonNull); + pub(crate) fn LLVMContextSetDiscardValueNames<'ll>(C: Context<'ll>, Discard: Bool); + pub(crate) fn LLVMGetMDKindIDInContext<'ll>( + C: Context<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". SLen: c_uint, ) -> MetadataKindId; @@ -905,9 +915,9 @@ unsafe extern "C" { pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull); // Create modules. - pub(crate) fn LLVMModuleCreateWithNameInContext( + pub(crate) fn LLVMModuleCreateWithNameInContext<'ll>( ModuleID: *const c_char, - C: &Context, + C: Context<'ll>, ) -> *const Module; pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; @@ -931,24 +941,24 @@ unsafe extern "C" { pub(crate) safe fn LLVMGetTypeKind(Ty: &Type) -> RawEnum; // Operations on integer types - pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; - pub(crate) safe fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; + pub(crate) fn LLVMInt1TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMInt8TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMInt16TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMInt32TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMInt64TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) safe fn LLVMIntTypeInContext<'ll>(C: Context<'ll>, NumBits: c_uint) -> &'ll Type; pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; // Operations on real types - pub(crate) fn LLVMHalfTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMFloatTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMHalfTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMFloatTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMDoubleTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMFP128TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; // Operations on non-IEEE real types - pub(crate) fn LLVMBFloatTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMPPCFP128TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMBFloatTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMPPCFP128TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; // Operations on function types pub(crate) fn LLVMFunctionType<'a>( @@ -963,15 +973,18 @@ unsafe extern "C" { pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool; // Operations on struct types - pub(crate) fn LLVMStructTypeInContext<'a>( - C: &'a Context, - ElementTypes: *const &'a Type, + pub(crate) fn LLVMStructTypeInContext<'ll>( + C: Context<'ll>, + ElementTypes: *const &'ll Type, ElementCount: c_uint, Packed: Bool, - ) -> &'a Type; + ) -> &'ll Type; // Operations on array, pointer, and vector types (sequence types) - pub(crate) safe fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + pub(crate) safe fn LLVMPointerTypeInContext<'ll>( + C: Context<'ll>, + AddressSpace: c_uint, + ) -> &'ll Type; pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; pub(crate) fn LLVMScalableVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; @@ -979,7 +992,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; // Operations on other types - pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMVoidTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; // Operations on all values pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; @@ -1013,16 +1026,16 @@ unsafe extern "C" { pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; // Operations on metadata - pub(crate) fn LLVMMDStringInContext2( - C: &Context, + pub(crate) fn LLVMMDStringInContext2<'ll>( + C: Context<'ll>, Str: *const c_uchar, // See "PTR_LEN_STR". SLen: size_t, - ) -> &Metadata; - pub(crate) fn LLVMMDNodeInContext2<'a>( - C: &'a Context, - Vals: *const &'a Metadata, + ) -> &'ll Metadata; + pub(crate) fn LLVMMDNodeInContext2<'ll>( + C: Context<'ll>, + Vals: *const &'ll Metadata, Count: size_t, - ) -> &'a Metadata; + ) -> &'ll Metadata; pub(crate) fn LLVMAddNamedMetadataOperand<'a>( M: &'a Module, Name: *const c_char, @@ -1046,18 +1059,18 @@ unsafe extern "C" { Length: u64, ) -> &'a Value; pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; - pub(crate) fn LLVMConstStringInContext2( - C: &Context, + pub(crate) fn LLVMConstStringInContext2<'ll>( + C: Context<'ll>, Str: *const c_uchar, // See "PTR_LEN_STR". Length: size_t, DontNullTerminate: Bool, - ) -> &Value; - pub(crate) fn LLVMConstStructInContext<'a>( - C: &'a Context, - ConstantVals: *const &'a Value, + ) -> &'ll Value; + pub(crate) fn LLVMConstStructInContext<'ll>( + C: Context<'ll>, + ConstantVals: *const &'ll Value, Count: c_uint, Packed: Bool, - ) -> &'a Value; + ) -> &'ll Value; pub(crate) fn LLVMConstNamedStruct<'a>( StructTy: &'a Type, ConstantVals: *const &'a Value, @@ -1121,13 +1134,13 @@ unsafe extern "C" { pub(crate) fn LLVMGetNextGlobalAlias(GlobalAlias: &Value) -> Option<&Value>; // Operations on attributes - pub(crate) fn LLVMCreateStringAttribute( - C: &Context, + pub(crate) fn LLVMCreateStringAttribute<'ll>( + C: Context<'ll>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: c_uint, Value: *const c_uchar, // See "PTR_LEN_STR". ValueLen: c_uint, - ) -> &Attribute; + ) -> &'ll Attribute; // Operations on functions pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); @@ -1169,11 +1182,11 @@ unsafe extern "C" { // Operations on basic blocks pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; - pub(crate) fn LLVMAppendBasicBlockInContext<'a>( - C: &'a Context, - Fn: &'a Value, + pub(crate) fn LLVMAppendBasicBlockInContext<'ll>( + C: Context<'ll>, + Fn: &'ll Value, Name: *const c_char, - ) -> &'a BasicBlock; + ) -> &'ll BasicBlock; // Operations on instructions pub(crate) fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; @@ -1662,7 +1675,7 @@ unsafe extern "C" { pub(crate) fn LLVMIsMultithreaded() -> Bool; - pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; + pub(crate) fn LLVMStructCreateNamed<'ll>(C: Context<'ll>, Name: *const c_char) -> &'ll Type; pub(crate) fn LLVMStructSetBody<'a>( StructTy: &'a Type, @@ -1674,7 +1687,7 @@ unsafe extern "C" { pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint; pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type); - pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; + pub(crate) safe fn LLVMMetadataAsValue<'ll>(C: Context<'ll>, MD: &'ll Metadata) -> &'ll Value; pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); @@ -1764,7 +1777,7 @@ unsafe extern "C" { ) -> &'ll Metadata; pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( - Ctx: &'ll Context, + Ctx: Context<'ll>, Line: c_uint, Column: c_uint, Scope: &'ll Metadata, @@ -2019,33 +2032,54 @@ unsafe extern "C" { ) -> Option<&Value>; // Operations on attributes - pub(crate) fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute; - pub(crate) fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateByRefAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; - pub(crate) fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; - pub(crate) fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateMemoryEffectsAttr( - C: &Context, + pub(crate) fn LLVMRustCreateAttrNoValue<'ll>( + C: Context<'ll>, + attr: AttributeKind, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateAlignmentAttr<'ll>(C: Context<'ll>, bytes: u64) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateDereferenceableAttr<'ll>( + C: Context<'ll>, + bytes: u64, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr<'ll>( + C: Context<'ll>, + bytes: u64, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateByValAttr<'ll>(C: Context<'ll>, ty: &'ll Type) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateByRefAttr<'ll>(C: Context<'ll>, ty: &'ll Type) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateStructRetAttr<'ll>( + C: Context<'ll>, + ty: &'ll Type, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateElementTypeAttr<'ll>( + C: Context<'ll>, + ty: &'ll Type, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateUWTableAttr<'ll>(C: Context<'ll>, async_: bool) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateAllocSizeAttr<'ll>( + C: Context<'ll>, + size_arg: u32, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateAllocKindAttr<'ll>( + C: Context<'ll>, + size_arg: u64, + ) -> &'ll Attribute; + pub(crate) fn LLVMRustCreateMemoryEffectsAttr<'ll>( + C: Context<'ll>, effects: MemoryEffects, - ) -> &Attribute; + ) -> &'ll Attribute; /// ## Safety /// - Each of `LowerWords` and `UpperWords` must point to an array that is /// long enough to fully define an integer of size `NumBits`, i.e. each /// pointer must point to `NumBits.div_ceil(64)` elements or more. /// - The implementation will make its own copy of the pointed-to `u64` /// values, so the pointers only need to outlive this function call. - pub(crate) fn LLVMRustCreateRangeAttribute( - C: &Context, + pub(crate) fn LLVMRustCreateRangeAttribute<'ll>( + C: Context<'ll>, NumBits: c_uint, LowerWords: *const u64, UpperWords: *const u64, - ) -> &Attribute; + ) -> &'ll Attribute; // Operations on functions pub(crate) fn LLVMRustAddFunctionAttributes<'a>( @@ -2560,12 +2594,12 @@ unsafe extern "C" { Target: &TargetMachine, ) -> bool; pub(crate) fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); - pub(crate) fn LLVMRustParseBitcodeForLTO( - Context: &Context, + pub(crate) fn LLVMRustParseBitcodeForLTO<'ll>( + Context: Context<'ll>, Data: *const u8, len: usize, Identifier: *const c_char, - ) -> Option<&Module>; + ) -> Option<&'ll Module>; pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( @@ -2581,20 +2615,20 @@ unsafe extern "C" { ); /// Removes the currently-installed `DiagnosticHandler`, and returns an owning pointer to it. - pub(crate) fn LLVMRustContextTakeDiagnosticHandler( - Context: &Context, + pub(crate) fn LLVMRustContextTakeDiagnosticHandler<'ll>( + Context: Context<'ll>, ) -> Option>; /// Installs a diagnostic handler that was previously removed by /// [`LLVMRustContextTakeDiagnosticHandler`]. - pub(crate) fn LLVMRustContextSetDiagnosticHandler( - context: &Context, + pub(crate) fn LLVMRustContextSetDiagnosticHandler<'ll>( + context: Context<'ll>, diagnostic_handler: Option>, ); /// Installs a custom diagnostic handler. - pub(crate) fn LLVMRustContextConfigureDiagnosticHandler( - context: &Context, + pub(crate) fn LLVMRustContextConfigureDiagnosticHandler<'ll>( + context: Context<'ll>, diagnostic_handler_callback: DiagnosticHandlerCallback, diagnostic_handler_callback_data: *mut c_void, remark_all_passes: bool, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index e189dc758d8e7..54b31bc450cfb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -63,7 +63,7 @@ pub(crate) fn AddCallSiteAttributes<'ll>( } pub(crate) fn CreateAttrStringValue<'ll>( - llcx: &'ll Context, + llcx: Context<'ll>, attr: &str, value: &str, ) -> &'ll Attribute { @@ -78,7 +78,7 @@ pub(crate) fn CreateAttrStringValue<'ll>( } } pub(crate) fn CreateAttrStringValueFromCStr<'ll>( - llcx: &'ll Context, + llcx: Context<'ll>, attr: &std::ffi::CStr, value: &std::ffi::CStr, ) -> &'ll Attribute { @@ -95,7 +95,7 @@ pub(crate) fn CreateAttrStringValueFromCStr<'ll>( } } -pub(crate) fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attribute { +pub(crate) fn CreateAttrString<'ll>(llcx: Context<'ll>, attr: &str) -> &'ll Attribute { unsafe { LLVMCreateStringAttribute( llcx, @@ -107,43 +107,53 @@ pub(crate) fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attr } } -pub(crate) fn CreateAlignmentAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateAlignmentAttr<'ll>(llcx: Context<'ll>, bytes: u64) -> &'ll Attribute { unsafe { LLVMRustCreateAlignmentAttr(llcx, bytes) } } -pub(crate) fn CreateDereferenceableAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateDereferenceableAttr<'ll>(llcx: Context<'ll>, bytes: u64) -> &'ll Attribute { unsafe { LLVMRustCreateDereferenceableAttr(llcx, bytes) } } -pub(crate) fn CreateDereferenceableOrNullAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateDereferenceableOrNullAttr<'ll>( + llcx: Context<'ll>, + bytes: u64, +) -> &'ll Attribute { unsafe { LLVMRustCreateDereferenceableOrNullAttr(llcx, bytes) } } -pub(crate) fn CreateByValAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { +pub(crate) fn CreateByValAttr<'ll>(llcx: Context<'ll>, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateByValAttr(llcx, ty) } } -pub(crate) fn CreateByRefAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { +pub(crate) fn CreateByRefAttr<'ll>(llcx: Context<'ll>, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateByRefAttr(llcx, ty) } } -pub(crate) fn CreateStructRetAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { +pub(crate) fn CreateStructRetAttr<'ll>(llcx: Context<'ll>, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateStructRetAttr(llcx, ty) } } -pub(crate) fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute { +pub(crate) fn CreateUWTableAttr<'ll>(llcx: Context<'ll>, async_: bool) -> &'ll Attribute { unsafe { LLVMRustCreateUWTableAttr(llcx, async_) } } -pub(crate) fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute { +pub(crate) fn CreateAllocSizeAttr<'ll>(llcx: Context<'ll>, size_arg: u32) -> &'ll Attribute { unsafe { LLVMRustCreateAllocSizeAttr(llcx, size_arg) } } -pub(crate) fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute { +pub(crate) fn CreateAllocKindAttr<'ll>( + llcx: Context<'ll>, + kind_arg: AllocKindFlags, +) -> &'ll Attribute { unsafe { LLVMRustCreateAllocKindAttr(llcx, kind_arg.bits()) } } -pub(crate) fn CreateRangeAttr(llcx: &Context, size: Size, range: WrappingRange) -> &Attribute { +pub(crate) fn CreateRangeAttr<'ll>( + llcx: Context<'ll>, + size: Size, + range: WrappingRange, +) -> &'ll Attribute { let lower = range.start; // LLVM treats the upper bound as exclusive, but allows wrapping. let upper = range.end.wrapping_add(1); @@ -229,14 +239,14 @@ pub(crate) fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) { impl AttributeKind { /// Create an LLVM Attribute with no associated value. - pub(crate) fn create_attr(self, llcx: &Context) -> &Attribute { + pub(crate) fn create_attr<'ll>(self, llcx: Context<'ll>) -> &'ll Attribute { unsafe { LLVMRustCreateAttrNoValue(llcx, self) } } } impl MemoryEffects { /// Create an LLVM Attribute with these memory effects. - pub(crate) fn create_attr(self, llcx: &Context) -> &Attribute { + pub(crate) fn create_attr<'ll>(self, llcx: Context<'ll>) -> &'ll Attribute { unsafe { LLVMRustCreateMemoryEffectsAttr(llcx, self) } } } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 4aa768019f696..2d64c63cf9ae4 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -131,7 +131,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// since for those we don't care about target architecture anyway. /// /// So, this function is for static aliases. See [`add_function_aliases`](Self::add_function_aliases) for the alternative. - fn add_static_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) { + fn add_static_aliases( + &self, + aliasee: &'ll llvm::Value, + aliases: &[(DefId, Linkage, Visibility)], + ) { let ty = self.get_type_of_global(aliasee); for (alias, linkage, visibility) in aliases { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index f03eb3d4a36fa..895286487ad35 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -149,7 +149,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { - pub(crate) fn llcx(&self) -> &'ll llvm::Context { + pub(crate) fn llcx(&self) -> llvm::Context<'ll> { (**self).borrow().llcx } @@ -286,12 +286,12 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } } -pub(crate) fn llvm_type_ptr(llcx: &llvm::Context) -> &Type { +pub(crate) fn llvm_type_ptr<'ll>(llcx: llvm::Context<'ll>) -> &'ll Type { llvm_type_ptr_in_address_space(llcx, AddressSpace::ZERO) } pub(crate) fn llvm_type_ptr_in_address_space<'ll>( - llcx: &'ll llvm::Context, + llcx: llvm::Context<'ll>, addr_space: AddressSpace, ) -> &'ll Type { llvm::LLVMPointerTypeInContext(llcx, addr_space.0) diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 9988ccfaee052..e781ffea99479 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -6,10 +6,10 @@ use crate::attributes; use crate::context::FullCx; use crate::llvm::{self, EnzymeWrapper, Value}; -fn to_enzyme_typetree( +fn to_enzyme_typetree<'ll>( rust_typetree: &RustTypeTree, _data_layout: &str, - llcx: &llvm::Context, + llcx: llvm::Context<'ll>, ) -> (llvm::TypeTree, Vec) { let mut enzyme_tt = llvm::TypeTree::new(); let extra_ints = process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); @@ -24,11 +24,11 @@ fn to_enzyme_typetree( (enzyme_tt, int_vec) } -fn process_typetree_recursive( +fn process_typetree_recursive<'ll>( enzyme_tt: &mut llvm::TypeTree, rust_typetree: &RustTypeTree, parent_indices: &[i64], - llcx: &llvm::Context, + llcx: llvm::Context<'ll>, ) -> u32 { let mut extra_ints = 0; for rust_type in &rust_typetree.0 { From d11be6b2330759dbf2f27b7198a1bac8079996d4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 26 Oct 2026 08:33:07 +0100 Subject: [PATCH 06/13] fd-views: enable test on Miri --- library/std/src/os/fd/owned.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index dd6336748db2c..b292c720a11cd 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -94,11 +94,7 @@ impl BorrowedFd<'_> { /// Converts a `BorrowedFd` into a reference to a file-like type. /// #[cfg_attr(all(unix, not(miri)), doc = "```")] - #[cfg_attr( - // FIXME: https://github.com/rust-lang/miri/issues/5453 - any(not(unix), miri), - doc = "```ignore (needs unix; miri doesn't support metadata on stdio)" - )] + #[cfg_attr(not(unix), doc = "```ignore (needs unix)")] /// # #![feature(fd_view)] /// # use std::fs::File; /// # use std::io; From 7c12d6c343374ae7676995543d07dab988463858 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 26 Oct 2026 09:30:20 +0100 Subject: [PATCH 07/13] x86-64-linux-gnu seems to have working f128 now --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index ea91756b9068a..b67f6ca851177 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -444,19 +444,18 @@ fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { // `long double` is not IEEE binary128. See // . // - // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits - // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` - // (ld is 80-bit extended precision). - // + // This rules out anything that doesn't have `long double` = `binary128`: <= 32 bits + // (`long double` is `f64`) and anything other than Linux (Windows and MacOS use `f64`). + // x86-64 would also be a problem, except there's a special fix for linux-gnu. + // We have to reject musl anyway because it doesn't have the symbols for f128 math. + _ if target_pointer_width <= 32 => false, + _ if *target_os != Os::Linux => false, + _ if *target_env != Env::Gnu => false, // On big-endian powerpc the symbol selection is correct, despite __ibmf128 being // long double on the target, but the f128 symbols are not defined. - // - // musl does not implement the symbols required for f128 math at all. - _ if *target_env == Env::Musl => false, - (Arch::X86_64, _) => false, (Arch::PowerPC | Arch::PowerPC64, _) if *target_endian == Endian::Big => false, - (_, Os::Linux) if target_pointer_width == 64 => true, - _ => false, + // After applying all these exceptions, the rest should work. + _ => true, } && cfg.has_reliable_f128; } From 21f918cbaf2d65d2bdbf7db01f2dbc4738d28cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 15 Mar 2026 01:23:27 +0000 Subject: [PATCH 08/13] Lex lifetimes using emoji and emit appropriate error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lex and parse emoji in lifetimes, and disallow them in the parser with a hard error. Allow emoji to start a lifetime name even if they are not XID_Start. ``` error: lifetimes cannot contain emoji --> $DIR/emoji-in-lifetime.rs:1:22 | LL | fn bad_lifetime_name<'๐Ÿ›๐Ÿ›๐Ÿ›family๐Ÿ‘จ๐Ÿ‘ฉ๐Ÿ‘ง๐Ÿ‘ฆ>( | ^^^^^^^^^^^^^^^^^^^^^ ``` Unify lifetime and identifier parsing. Use `bad_unicode_identifiers` for lifetimes. --- Cargo.lock | 1 + compiler/rustc_lexer/src/lib.rs | 38 ++++++++++------- compiler/rustc_parse/Cargo.toml | 1 + compiler/rustc_parse/src/lexer/mod.rs | 35 +++++++++++++--- .../crates/parser/src/lexed_str.rs | 2 +- .../err/lifetime_starts_with_a_number.rast | 4 +- .../err/lifetime_starts_with_a_number.txt | 4 +- .../lexer/err/unclosed_char_with_ferris.rast | 2 +- tests/ui/lexer/emoji-in-lifetime.rs | 16 ++++++++ tests/ui/lexer/emoji-in-lifetime.stderr | 41 +++++++++++++++++++ .../lex-bad-str-literal-as-char-1.stderr | 2 +- tests/ui/parser/numeric-lifetime.stderr | 4 +- 12 files changed, 121 insertions(+), 29 deletions(-) create mode 100644 tests/ui/lexer/emoji-in-lifetime.rs create mode 100644 tests/ui/lexer/emoji-in-lifetime.stderr diff --git a/Cargo.lock b/Cargo.lock index 41c82c989becc..5ff455c57f056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4640,6 +4640,7 @@ dependencies = [ "thin-vec", "tracing", "unicode-normalization", + "unicode-properties", "unicode-width 0.2.2", ] diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 043b7ad59d0a8..0aa57e75889fa 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -143,6 +143,9 @@ pub enum TokenKind { /// A lifetime, e.g. `'a`. Lifetime { + // FIXME: this should be renamed to `invalid` as it also signals when + // there are emoji in the lifetime name, but avoid changing the name so + // we don't introduce incompatibilities with rust-analyzer. starts_with_number: bool, }, @@ -591,7 +594,7 @@ impl<'a> Cursor<'a> { let kind = RawStr { n_hashes: res.ok() }; Literal { kind, suffix_start } } - _ => self.ident_or_unknown_prefix(), + _ => self.ident_or_unknown_prefix(false), }, // Forced keyword identifier or identifier. @@ -601,7 +604,7 @@ impl<'a> Cursor<'a> { self.eat_identifier(); ForcedKeywordIdent } - _ => self.ident_or_unknown_prefix(), + _ => self.ident_or_unknown_prefix(false), }, // Byte literal, byte string literal, raw byte string literal or identifier. @@ -620,7 +623,7 @@ impl<'a> Cursor<'a> { // Identifier (this should be checked after other variant that can // start as identifier). - c if is_id_start(c) => self.ident_or_unknown_prefix(), + c if is_id_start(c) => self.ident_or_unknown_prefix(false), // Numeric literal. c @ '0'..='9' => { @@ -678,7 +681,7 @@ impl<'a> Cursor<'a> { Literal { kind, suffix_start } } // Identifier starting with an emoji. Only lexed for graceful error recovery. - c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(), + c if is_emoji(c) => self.invalid_ident(), _ => Unknown, }; if matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes) @@ -840,25 +843,22 @@ impl<'a> Cursor<'a> { Whitespace } - fn ident_or_unknown_prefix(&mut self) -> TokenKind { - debug_assert!(is_id_start(self.prev())); + fn ident_or_unknown_prefix(&mut self, already_invalid: bool) -> TokenKind { + debug_assert!(is_id_start(self.prev()) || already_invalid); // Start is already eaten, eat the rest of identifier. self.eat_while(is_id_continue); // Known prefixes must have been handled earlier. So if // we see a prefix here, it is definitely an unknown prefix. match self.first() { '#' | '"' | '\'' => UnknownPrefix, - c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(), + c if is_emoji(c) => self.invalid_ident(), _ => Ident, } } fn invalid_ident(&mut self) -> TokenKind { // Start is already eaten, eat the rest of identifier. - self.eat_while(|c| { - const ZERO_WIDTH_JOINER: char = '\u{200d}'; - is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER - }); + self.eat_while(|c| is_id_continue(c) || is_emoji(c)); // An invalid identifier followed by '#' or '"' or '\'' could be // interpreted as an invalid literal prefix. We don't bother doing that // because the treatment of invalid identifiers and invalid prefixes @@ -903,7 +903,7 @@ impl<'a> Cursor<'a> { let kind = mk_kind_raw(res.ok()); Literal { kind, suffix_start } } - _ => self.ident_or_unknown_prefix(), + _ => self.ident_or_unknown_prefix(false), } } @@ -983,6 +983,7 @@ impl<'a> Cursor<'a> { fn lifetime_or_char(&mut self) -> TokenKind { debug_assert!(self.prev() == '\''); + let mut invalid = false; let can_be_a_lifetime = if self.second() == '\'' { // It's surely not a lifetime. false @@ -990,7 +991,10 @@ impl<'a> Cursor<'a> { // If the first symbol is valid for identifier, it can be a lifetime. // Also check if it's a number for a better error reporting (so '0 will // be reported as invalid lifetime and not as unterminated char literal). - is_id_start(self.first()) || self.first().is_ascii_digit() + let c = self.first(); + invalid |= c.is_ascii_digit(); + invalid |= is_emoji(c); + is_id_start(c) || invalid }; if !can_be_a_lifetime { @@ -1020,7 +1024,7 @@ impl<'a> Cursor<'a> { // First symbol can be a number (which isn't a valid identifier start), // so skip it without any checks. self.bump(); - self.eat_while(is_id_continue); + invalid |= matches!(self.ident_or_unknown_prefix(invalid), InvalidIdent); match self.first() { // Check if after skipping literal contents we've met a closing @@ -1032,7 +1036,7 @@ impl<'a> Cursor<'a> { Literal { kind, suffix_start: self.pos_within_token() } } '#' if !starts_with_number => UnknownPrefixLifetime, - _ => Lifetime { starts_with_number }, + _ => Lifetime { starts_with_number: invalid }, } } @@ -1285,3 +1289,7 @@ impl<'a> Cursor<'a> { self.eat_while(is_id_continue); } } + +fn is_emoji(c: char) -> bool { + !c.is_ascii() && c.is_emoji_char() +} diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 9650742628f73..b54438384a215 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -21,6 +21,7 @@ rustc_span = { path = "../rustc_span" } thin-vec = "0.2.21" tracing = "0.1" unicode-normalization = "0.1.25" +unicode-properties = { version = "0.1.4", default-features = false, features = ["emoji"] } unicode-width = "0.2.2" # tidy-alphabetical-end diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 03d5634fc359c..13bda08fe8096 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -17,6 +17,7 @@ use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use tracing::debug; +use unicode_properties::emoji::UnicodeEmoji; use crate::lexer::diagnostics::TokenTreeDiagInfo; use crate::lexer::unicode_chars::UNICODE_ARRAY; @@ -357,12 +358,36 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // this is necessary. let lifetime_name = nfc_normalize(self.str_from(start)); self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); + let span = self.mk_sp(start, self.pos); if starts_with_number { - let span = self.mk_sp(start, self.pos); - self.dcx() - .struct_err("lifetimes cannot start with a number") - .with_span(span) - .stash(span, StashKey::LifetimeIsChar); + let name = lifetime_name.as_str(); + // skip(1) to skip the `'` + let starts_with_number = matches!( + name.chars().skip(1).next(), + Some(c) if c.is_ascii_digit() + ); + if name.chars().any(|c| !c.is_ascii() && c.is_emoji_char()) { + self.psess + .bad_unicode_identifiers + .borrow_mut() + .entry(lifetime_name) + .or_default() + .push(span); + } + if starts_with_number { + let mut err = self.dcx() + .struct_err(format!( + "lifetimes cannot start with a number: `{name}`" + )) + .with_span(span); + if name.len() > 2 { + // Point at the first lifetime name character. + let start_span = self.mk_sp(start + BytePos(1), start + BytePos(2)); + err.span(start_span); + err.span_label(span, ""); + } + err.stash(span, StashKey::LifetimeIsChar); + } } token::Lifetime(lifetime_name, IdentKind::Normal) } diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index f449809911c90..9b9437740923c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -267,7 +267,7 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Lifetime { starts_with_number } => { if *starts_with_number { - errors.push("Lifetime name cannot start with a number".into()); + errors.push("Lifetime name contains invalid characters".into()); } LIFETIME_IDENT } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.rast index e919bf2a4aef2..b2bf087e749f3 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.rast @@ -1,4 +1,4 @@ -LIFETIME_IDENT "'1" error: Lifetime name cannot start with a number +LIFETIME_IDENT "'1" error: Lifetime name contains invalid characters WHITESPACE "\n" -LIFETIME_IDENT "'1lifetime" error: Lifetime name cannot start with a number +LIFETIME_IDENT "'1lifetime" error: Lifetime name contains invalid characters WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.txt b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.txt index e919bf2a4aef2..b2bf087e749f3 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.txt +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/lifetime_starts_with_a_number.txt @@ -1,4 +1,4 @@ -LIFETIME_IDENT "'1" error: Lifetime name cannot start with a number +LIFETIME_IDENT "'1" error: Lifetime name contains invalid characters WHITESPACE "\n" -LIFETIME_IDENT "'1lifetime" error: Lifetime name cannot start with a number +LIFETIME_IDENT "'1lifetime" error: Lifetime name contains invalid characters WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_char_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_char_with_ferris.rast index 56f19cce0784e..6ace31f434ef2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_char_with_ferris.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_char_with_ferris.rast @@ -1 +1 @@ -CHAR "'๐Ÿฆ€" error: Missing trailing `'` symbol to terminate the character literal +LIFETIME_IDENT "'๐Ÿฆ€" error: Lifetime name contains invalid characters diff --git a/tests/ui/lexer/emoji-in-lifetime.rs b/tests/ui/lexer/emoji-in-lifetime.rs new file mode 100644 index 0000000000000..e4a12f332ae70 --- /dev/null +++ b/tests/ui/lexer/emoji-in-lifetime.rs @@ -0,0 +1,16 @@ +// #141081 +fn bad_lifetime_name< + '๐Ÿ›๐Ÿ›๐Ÿ›family๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ,//~ ERROR: identifiers cannot contain emoji + '12, //~ ERROR: lifetimes cannot start with a number + 'a๐Ÿ›, //~ ERROR: identifiers cannot contain emoji + '1๐Ÿ›, //~ ERROR: identifiers cannot contain emoji + //~^ ERROR: lifetimes cannot start with a number + '1, //~ ERROR: lifetimes cannot start with a number + 'aโ€Œb // bare zero-width-joiners are accepted as XID_Continue +>() {} + +fn main() { + 'a๐Ÿ›: { // pointed at on the error from line 5 + todo!(); + }; +} diff --git a/tests/ui/lexer/emoji-in-lifetime.stderr b/tests/ui/lexer/emoji-in-lifetime.stderr new file mode 100644 index 0000000000000..7e205639a9a3c --- /dev/null +++ b/tests/ui/lexer/emoji-in-lifetime.stderr @@ -0,0 +1,41 @@ +error: identifiers cannot contain emoji: `'๐Ÿ›๐Ÿ›๐Ÿ›family๐Ÿ‘จ๐Ÿ‘ฉ๐Ÿ‘ง๐Ÿ‘ฆ` + --> $DIR/emoji-in-lifetime.rs:3:5 + | +LL | '๐Ÿ›๐Ÿ›๐Ÿ›family๐Ÿ‘จ๐Ÿ‘ฉ๐Ÿ‘ง๐Ÿ‘ฆ, + | ^^^^^^^^^^^^^^^^^^^^^ + +error: identifiers cannot contain emoji: `'a๐Ÿ›` + --> $DIR/emoji-in-lifetime.rs:5:5 + | +LL | 'a๐Ÿ›, + | ^^^^ +... +LL | 'a๐Ÿ›: { // pointed at on the error from line 5 + | ^^^^ + +error: identifiers cannot contain emoji: `'1๐Ÿ›` + --> $DIR/emoji-in-lifetime.rs:6:5 + | +LL | '1๐Ÿ›, + | ^^^^ + +error: lifetimes cannot start with a number: `'12` + --> $DIR/emoji-in-lifetime.rs:4:6 + | +LL | '12, + | -^- + +error: lifetimes cannot start with a number: `'1๐Ÿ›` + --> $DIR/emoji-in-lifetime.rs:6:6 + | +LL | '1๐Ÿ›, + | -^-- + +error: lifetimes cannot start with a number: `'1` + --> $DIR/emoji-in-lifetime.rs:8:5 + | +LL | '1, + | ^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr index 81ee697802b9c..7503774cd686f 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -10,7 +10,7 @@ LL - println!('1 + 1'); LL + println!("1 + 1"); | -error: lifetimes cannot start with a number +error: lifetimes cannot start with a number: `'1` --> $DIR/lex-bad-str-literal-as-char-1.rs:3:14 | LL | println!('1 + 1'); diff --git a/tests/ui/parser/numeric-lifetime.stderr b/tests/ui/parser/numeric-lifetime.stderr index 7c1bcb7263171..46b06ae7dc3cb 100644 --- a/tests/ui/parser/numeric-lifetime.stderr +++ b/tests/ui/parser/numeric-lifetime.stderr @@ -6,13 +6,13 @@ LL | let x: usize = ""; | | | expected due to this -error: lifetimes cannot start with a number +error: lifetimes cannot start with a number: `'1` --> $DIR/numeric-lifetime.rs:1:10 | LL | struct S<'1> { s: &'1 usize } | ^^ -error: lifetimes cannot start with a number +error: lifetimes cannot start with a number: `'1` --> $DIR/numeric-lifetime.rs:1:20 | LL | struct S<'1> { s: &'1 usize } From 39d7cc15127a5eb4f89b2cc684892a0e2ddfc617 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Mon, 26 Oct 2026 20:23:09 +0100 Subject: [PATCH 09/13] docs: Nitpick change in `HashMap`'s `.insert_entry()` doc example During work on PR 164772 [1], I've noticed another tiny inconsistency in doc code examples: `HashMap`'s `Entry::insert_entry()`'s doc code example did not have an assertion where `BTreeMap`'s `Entry::insert_entry()`'s docs did. I've added an assertion where it was missing. [1] https://github.com/rust-lang/rust/pull/164772 --- library/std/src/collections/hash/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index d95fcf38a3b49..d7bf89278d0d1 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -2988,7 +2988,8 @@ impl<'a, K: 'a, V: 'a, A: Allocator> VacantEntry<'a, K, V, A> { /// let mut map: HashMap<&str, u32> = HashMap::new(); /// /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert_entry(37); + /// let entry = o.insert_entry(37); + /// assert_eq!(entry.get(), &37); /// } /// assert_eq!(map["poneyland"], 37); /// ``` From b6c9f78a9e2166aa1cae5bf27ca5d27168492d3a Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 19 Oct 2026 17:18:26 -0500 Subject: [PATCH 10/13] core: Split the `resumable_breakpoint` intrinsic and constify it This function is documented as allowed to continue, which is useful behavior during const eval. Split a new `resumable_breakpoint` intrinsic from `breakpoint` which has the behavior of continuing in Miri and CTFE. --- ...0027-sysroot_tests-128bit-atomic-operations.patch | 2 +- .../rustc_codegen_cranelift/src/intrinsics/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +++- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 3 ++- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 10 +++++++++- library/core/src/process.rs | 4 ++-- library/coretests/tests/lib.rs | 3 +++ library/coretests/tests/process.rs | 12 ++++++++++++ 10 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 library/coretests/tests/process.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7194d8144ca69..6ec25732ab767 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -18,8 +18,8 @@ index 1e336bf..35e6f54 100644 // tidy-alphabetical-start #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] + #![feature(abort_immediate)] #![feature(array_ptr_get)] - #![feature(array_try_from_fn)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs index b735957..ea728b6 100644 --- a/coretests/tests/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 1437ea7445d24..2d84352a064aa 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -474,7 +474,7 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().trap(TrapCode::user(2).unwrap()); return Ok(()); } - sym::breakpoint => { + sym::breakpoint | sym::resumable_breakpoint => { intrinsic_args!(fx, args => (); intrinsic); fx.bcx.ins().debugtrap(); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 3c284ac918040..05f1a0c6a5bd6 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -312,7 +312,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc ); return IntrinsicResult::WroteIntoPlace; } - sym::breakpoint => { + sym::breakpoint | sym::resumable_breakpoint => { unimplemented!(); } sym::va_arg => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6fcdd6e9572ad..e9b7856b51568 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -296,7 +296,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[1].immediate(), args[2].immediate(), ), - sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), + sym::breakpoint | sym::resumable_breakpoint => { + self.call_intrinsic("llvm.debugtrap", &[], &[]) + } sym::va_arg => { let target = &self.cx.tcx.sess.target; let stability = target.supports_c_variadic_definitions(); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 804e84324f0d2..73f2d4b6c5714 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -170,6 +170,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ptr_guaranteed_cmp | sym::ptr_mask | sym::ptr_metadata + | sym::resumable_breakpoint | sym::return_address | sym::rotate_left | sym::rotate_right @@ -293,7 +294,7 @@ pub(crate) fn check_intrinsic_type( sym::abort_immediate => (0, 0, vec![], tcx.types.never), sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), - sym::breakpoint => (0, 0, vec![], tcx.types.unit), + sym::breakpoint | sym::resumable_breakpoint => (0, 0, vec![], tcx.types.unit), sym::codeview_annotation => (1, 0, vec![], tcx.types.unit), sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4ab0a6d988b84..c0962e89ec00b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1768,6 +1768,7 @@ symbols! { residual, result, result_ffi_guarantees, + resumable_breakpoint, return_address, return_position_impl_trait_in_trait, return_type_notation, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 550e52480301c..0989ffd66ca8c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -566,11 +566,19 @@ pub const fn prefetch_write_instruction(data: *const T) /// Executes a breakpoint trap, for inspection by a debugger. /// -/// This intrinsic does not have a stable counterpart. +/// The public version of this intrinsic is [`core::arch::breakpoint`]. #[rustc_intrinsic] #[rustc_nounwind] pub fn breakpoint(); +/// Executes a breakpoint trap, for inspection by a debugger. This is allowed to resume. +/// +/// The public version of this intrinsic is [`core::process::resumable_breakpoint`]. +#[rustc_intrinsic] +#[rustc_nounwind] +#[miri::intrinsic_fallback_is_spec] +pub const fn resumable_breakpoint() {} + /// A container for the string arguments passed to [`codeview_annotation`]. #[unstable(feature = "codeview_annotation", issue = "163964")] pub trait CodeViewAnnotationArgs { diff --git a/library/core/src/process.rs b/library/core/src/process.rs index e92339f34d68e..906b99ffdb40d 100644 --- a/library/core/src/process.rs +++ b/library/core/src/process.rs @@ -58,6 +58,6 @@ pub const fn abort_immediate() -> ! { // actually want to stabilize both of them. #[unstable(feature = "breakpoint", issue = "133724")] #[inline(always)] -pub fn resumable_breakpoint() { - crate::intrinsics::breakpoint(); +pub const fn resumable_breakpoint() { + crate::intrinsics::resumable_breakpoint(); } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 9054f9331f7bf..3fe6349fec1c1 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] +#![feature(abort_immediate)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] #![feature(array_try_map)] @@ -10,6 +11,7 @@ #![feature(async_iterator)] #![feature(atomic_volatile)] #![feature(borrowed_buf_init)] +#![feature(breakpoint)] #![feature(byte_str)] #![feature(casefold)] #![feature(cfg_overflow_checks)] @@ -208,6 +210,7 @@ mod panic; mod pattern; mod pin; mod pin_macro; +mod process; mod ptr; mod result; mod simd; diff --git a/library/coretests/tests/process.rs b/library/coretests/tests/process.rs new file mode 100644 index 0000000000000..86cb594f0c62b --- /dev/null +++ b/library/coretests/tests/process.rs @@ -0,0 +1,12 @@ +use core::process; + +#[test] +fn const_breakpoint() { + // Ensure this continues in CTFE + const { process::resumable_breakpoint() }; + + // Miri should also continue, while native runtime will likely abort + if cfg!(miri) { + process::resumable_breakpoint(); + } +} From d0e5a142eef0702e7329ff47f6e614342c74d597 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 19 Oct 2026 17:20:28 -0500 Subject: [PATCH 11/13] std: Re-export core::process::resumable_breakpoint --- library/std/src/process.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index feed6e1ad55a0..7afda60d019ec 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2642,6 +2642,9 @@ pub fn abort() -> ! { #[doc(inline)] #[unstable(feature = "abort_immediate", issue = "154601")] pub use core::process::abort_immediate; +#[doc(inline)] +#[unstable(feature = "breakpoint", issue = "133724")] +pub use core::process::resumable_breakpoint; /// Returns the OS-assigned process identifier associated with this process. /// From 1b97c28b5aeb2135bd46fc367ab84debc2c8721b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Sanz=20Gonz=C3=A1lez?= <80487270+hsanzg@users.noreply.github.com> Date: Sat, 24 Oct 2026 13:48:33 +0200 Subject: [PATCH 12/13] Add strict methods to `NonZero` The original `nonzero_ops` feature (https://github.com/rust-lang/rust/issues/84186) was stabilized before the `strict_*` methods on the regular integer types, so the inclusion of the `strict` methods on `NonZero` type was left as future work. Some users (see e.g. https://github.com/rust-lang/rust/issues/84186#issuecomment-4296692119) have recently requested these methods, and it's good to have them for consistency. --- library/core/src/num/nonzero.rs | 236 ++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d0cc0e3b31da7..99498252a127f 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1231,6 +1231,66 @@ macro_rules! nonzero_integer { } } + /// Multiplies two non-zero integers together, panicking if + /// overflow occurs. + /// + /// # Panics + /// + /// ## Overflow behavior + /// + /// This function always panics on overflow, regardless of whether + /// overflow checks are enabled. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ")?;")] + #[doc = concat!("let nine = NonZero::new(9", stringify!($Int), ")?;")] + /// + /// assert_eq!(nine, three.strict_mul(three)); + /// # Some(()) + /// # } + /// ``` + /// + /// The following panics because of overflow: + /// + /// ```should_panic + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ")?;")] + #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX)?;")] + /// + /// let _ = max.strict_mul(two); + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_strict_ops", issue = "164850")] + #[rustc_const_unstable(feature = "const_nonzero_strict_ops", issue = "164850")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[track_caller] + pub const fn strict_mul(self, other: Self) -> Self { + // SAFETY: + // - `$Int::strict_mul` panics on overflow; + // - `self` and `other` are nonzero; + // - the only way to get zero from a multiplication without + // overflow is for either operand to be zero. + // + // So the result cannot be zero. + unsafe { Self::new_unchecked(self.get().strict_mul(other.get())) } + } + /// Multiplies two non-zero integers together. #[doc = concat!("Return [`NonZero::<", stringify!($Int), ">::MAX`] on overflow.")] /// @@ -1344,6 +1404,65 @@ macro_rules! nonzero_integer { } } + /// Raises a non-zero value to an integer power, panicking if + /// overflow occurs. + /// + /// # Panics + /// + /// ## Overflow behavior + /// + /// This function always panics on overflow, regardless of whether + /// overflow checks are enabled. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ")?;")] + #[doc = concat!("let sixteen = NonZero::new(16", stringify!($Int), ")?;")] + /// + /// assert_eq!(sixteen, two.strict_pow(4)); + /// # Some(()) + /// # } + /// ``` + /// + /// The following panics because of overflow: + /// + /// ```should_panic + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let half_max = NonZero::new(", stringify!($Int), "::MAX / 2)?;")] + /// + /// let _ = half_max.strict_pow(2); + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_strict_ops", issue = "164850")] + #[rustc_const_unstable(feature = "const_nonzero_strict_ops", issue = "164850")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[track_caller] + pub const fn strict_pow(self, exp: u32) -> Self { + // SAFETY: + // - `$Int::strict_pow` panics on overflow; + // - `self` is nonzero; + // - the only way to get zero from an exponentiation without + // overflow is for the base to be zero. + // + // So the result cannot be zero. + unsafe { Self::new_unchecked(self.get().strict_pow(exp)) } + } + /// Raise non-zero value to an integer power. #[doc = sign_dependent_expr!{ $signedness ? @@ -1824,6 +1943,65 @@ macro_rules! nonzero_integer_signedness_dependent_methods { } } + /// Adds an unsigned integer to a non-zero value, panicking + /// if overflow occurs. + /// + /// # Panics + /// + /// ## Overflow behavior + /// + /// This function always panics on overflow, regardless of whether + /// overflow checks are enabled. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ")?;")] + #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ")?;")] + /// + /// assert_eq!(three, two.strict_add(1)); + /// # Some(()) + /// # } + /// ``` + /// + /// The following panics because of overflow: + /// + /// ```should_panic + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX)?;")] + /// + /// let _ = max.strict_add(1); + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_strict_ops", issue = "164850")] + #[rustc_const_unstable(feature = "const_nonzero_strict_ops", issue = "164850")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[track_caller] + pub const fn strict_add(self, other: $Int) -> Self { + // SAFETY: + // - `$Int::strict_add` panics on overflow; + // - `self` is nonzero; + // - the only way to get zero from an unsigned addition without + // overflow is for both addends to be zero. + // + // So the result cannot be zero. + unsafe { Self::new_unchecked(self.get().strict_add(other)) } + } + /// Adds an unsigned integer to a non-zero value. #[doc = concat!("Return [`NonZero::<", stringify!($Int), ">::MAX`] on overflow.")] /// @@ -2493,6 +2671,64 @@ macro_rules! nonzero_integer_signedness_dependent_methods { None } + /// Checked negation. Computes `-self`, panicking if `self == MIN`. + /// + /// # Panics + /// + /// ## Overflow behavior + /// + /// This function always panics on overflow, regardless of whether + /// overflow checks are enabled. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let pos_six = NonZero::new(6", stringify!($Int), ")?;")] + #[doc = concat!("let neg_six = NonZero::new(-6", stringify!($Int), ")?;")] + /// + /// assert_eq!(pos_six.strict_neg(), neg_six); + /// # Some(()) + /// # } + /// ``` + /// + /// The following panics because of overflow: + /// + /// ```should_panic + /// #![feature(nonzero_strict_ops)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let min = NonZero::new(", stringify!($Int), "::MIN)?;")] + /// + /// let _ = min.strict_neg(); + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_strict_ops", issue = "164850")] + #[rustc_const_unstable(feature = "const_nonzero_strict_ops", issue = "164850")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[track_caller] + pub const fn strict_neg(self) -> Self { + // SAFETY: + // - `$Int::strict_neg` panics on overflow; + // - `self` is nonzero; + // - the only way to get zero from a negation without overflow + // is for the input to be zero. + // + // So the result cannot be zero. + unsafe { Self::new_unchecked(self.get().strict_neg()) } + } + /// Negates self, overflowing if this is equal to the minimum value. /// #[doc = concat!("See [`", stringify!($Int), "::overflowing_neg`]")] From 7cd27a62a7edd710bb32284ddaf326d6d97ade45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Sanz=20Gonz=C3=A1lez?= <80487270+hsanzg@users.noreply.github.com> Date: Mon, 26 Oct 2026 00:07:04 +0100 Subject: [PATCH 13/13] Add `NonZero::strict_` tests via macro-generated modules To avoid further duplication in `nonzero.rs`, this new set of tests is generated by macros similar to those used for the primitive integer types in `coretests/tests/num/{u,}int_macros.rs`. Many of the tests in `nonzero.rs` are good candidates to be moved to the new `nonzero_{u,}int_module` macros, but that's left as future work. --- library/coretests/tests/lib.rs | 3 +- library/coretests/tests/num/mod.rs | 1 + library/coretests/tests/{ => num}/nonzero.rs | 3 + library/coretests/tests/num/nonzero/int.rs | 137 +++++++++++++++++++ library/coretests/tests/num/nonzero/uint.rs | 101 ++++++++++++++ 5 files changed, 244 insertions(+), 1 deletion(-) rename library/coretests/tests/{ => num}/nonzero.rs (99%) create mode 100644 library/coretests/tests/num/nonzero/int.rs create mode 100644 library/coretests/tests/num/nonzero/uint.rs diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index bd53680408882..dfe472d841efd 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -35,6 +35,7 @@ #![feature(const_eval_select)] #![feature(const_index)] #![feature(const_iter)] +#![feature(const_nonzero_strict_ops)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] @@ -92,6 +93,7 @@ #![feature(min_specialization)] #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] +#![feature(nonzero_strict_ops)] #![feature(num_internals)] #![feature(numfmt)] #![feature(one_sided_range)] @@ -200,7 +202,6 @@ mod macros; mod manually_drop; mod mem; mod net; -mod nonzero; mod num; mod ops; mod option; diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 46e5ee0213b04..919946fe4abcc 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -38,6 +38,7 @@ mod int_sqrt; mod midpoint; mod nan; mod niche_types; +mod nonzero; mod ops; mod wrapping; diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/num/nonzero.rs similarity index 99% rename from library/coretests/tests/nonzero.rs rename to library/coretests/tests/num/nonzero.rs index 37041e22d0dfd..388346fc8aca0 100644 --- a/library/coretests/tests/nonzero.rs +++ b/library/coretests/tests/num/nonzero.rs @@ -1,3 +1,6 @@ +mod int; +mod uint; + use core::num::{IntErrorKind, NonZero}; use core::option::Option::None; diff --git a/library/coretests/tests/num/nonzero/int.rs b/library/coretests/tests/num/nonzero/int.rs new file mode 100644 index 0000000000000..b46bfd3d585e6 --- /dev/null +++ b/library/coretests/tests/num/nonzero/int.rs @@ -0,0 +1,137 @@ +macro_rules! nonzero_int_module { + ($T:ident) => { + use core::num::NonZero; + const MIN: NonZero<$T> = NonZero::<$T>::MIN; + const MAX: NonZero<$T> = NonZero::<$T>::MAX; + + const fn nz(n: $T) -> NonZero<$T> { + NonZero::new(n).unwrap() + } + + test_runtime_and_compiletime! { + fn test_strict_neg() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_neg(), nz(-1)); + assert_eq_const_safe!(NonZero<$T>: nz(-1).strict_neg(), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(123).strict_neg(), nz(-123)); + assert_eq_const_safe!(NonZero<$T>: nz(-123).strict_neg(), nz(123)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MAX - 2).strict_neg(), nz($T::MIN + 3)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MAX - 1).strict_neg(), nz($T::MIN + 2)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_neg(), nz($T::MIN + 1)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MIN + 2).strict_neg(), nz($T::MAX - 1)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MIN + 1).strict_neg(), MAX); + } + + fn test_strict_mul() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_mul(nz(1)), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(-1).strict_mul(nz(1)), nz(-1)); + assert_eq_const_safe!(NonZero<$T>: nz(63).strict_mul(nz(-2)), nz(-126)); + assert_eq_const_safe!(NonZero<$T>: nz(-63).strict_mul(nz(-2)), nz(126)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_mul(nz(1)), MAX); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_mul(MAX), MAX); + assert_eq_const_safe!(NonZero<$T>: MIN.strict_mul(nz(1)), MIN); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_mul(MIN), MIN); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_mul(nz(-1)), nz($T::MIN + 1)); + assert_eq_const_safe!(NonZero<$T>: nz(-1).strict_mul(MAX), nz($T::MIN + 1)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MIN / 2).strict_mul(nz(2)), MIN); + assert_eq_const_safe!(NonZero<$T>: nz(2).strict_mul(nz($T::MIN / 2)), MIN); + assert_eq_const_safe!(NonZero<$T>: nz($T::MAX / 2).strict_mul(nz(2)), nz($T::MAX - 1)); + assert_eq_const_safe!(NonZero<$T>: nz(2).strict_mul(nz($T::MAX / 2)), nz($T::MAX - 1)); + } + + fn test_strict_pow() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_pow(1), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(-1).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(-1).strict_pow(1), nz(-1)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(1), nz(3)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(2), nz(9)); + assert_eq_const_safe!(NonZero<$T>: nz(-3).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(-3).strict_pow(1), nz(-3)); + assert_eq_const_safe!(NonZero<$T>: nz(-3).strict_pow(2), nz(9)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_pow(1), MAX); + assert_eq_const_safe!(NonZero<$T>: MIN.strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: MIN.strict_pow(1), MIN); + } + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn test_strict_neg_overflow() { + let _ = MIN.strict_neg(); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_neg_min() { + let _ = MIN.strict_mul(nz(-1)); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_neg_min_reverse() { + let _ = nz(-1).strict_mul(MIN); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_limit() { + let _ = nz($T::MAX / 2 + 1).strict_mul(nz(2)); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_large() { + let _ = MAX.strict_mul(nz(2)); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_neg_large() { + let _ = MIN.strict_mul(nz(2)); + } + + #[test] + #[should_panic = "attempt to exponentiate with overflow"] + fn test_strict_pow_overflow_big_base() { + let _ = MAX.strict_pow(2); + } + + #[test] + #[should_panic = "attempt to exponentiate with overflow"] + fn test_strict_pow_overflow_big_exp() { + let _ = nz(2).strict_pow(256); + } + + #[test] + #[should_panic = "attempt to exponentiate with overflow"] + fn test_strict_pow_overflow_neg_base() { + let _ = MIN.strict_pow(2); + } + }; +} + +mod i8 { + nonzero_int_module!(i8); +} + +mod i16 { + nonzero_int_module!(i16); +} + +mod i32 { + nonzero_int_module!(i32); +} + +mod i64 { + nonzero_int_module!(i64); +} + +mod i128 { + nonzero_int_module!(i128); +} + +mod isize { + nonzero_int_module!(isize); +} diff --git a/library/coretests/tests/num/nonzero/uint.rs b/library/coretests/tests/num/nonzero/uint.rs new file mode 100644 index 0000000000000..f1b47927ec00e --- /dev/null +++ b/library/coretests/tests/num/nonzero/uint.rs @@ -0,0 +1,101 @@ +macro_rules! nonzero_uint_module { + ($T:ident) => { + use core::num::NonZero; + const MAX: NonZero<$T> = NonZero::<$T>::MAX; + + const fn nz(n: $T) -> NonZero<$T> { + NonZero::new(n).unwrap() + } + + test_runtime_and_compiletime! { + fn test_strict_add() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_add(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_add(1), nz(2)); + assert_eq_const_safe!(NonZero<$T>: nz(57).strict_add(173), nz(230)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MAX - 1).strict_add(1), MAX); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_add($T::MAX - 1), MAX); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_add(0), MAX); + } + + fn test_strict_mul() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_mul(nz(1)), nz(1)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_mul(nz(1)), MAX); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_mul(MAX), MAX); + assert_eq_const_safe!(NonZero<$T>: nz(123).strict_mul(nz(2)), nz(246)); + assert_eq_const_safe!(NonZero<$T>: nz(2).strict_mul(nz(123)), nz(246)); + assert_eq_const_safe!(NonZero<$T>: nz($T::MAX / 2).strict_mul(nz(2)), nz($T::MAX - 1)); + assert_eq_const_safe!(NonZero<$T>: nz(2).strict_mul(nz($T::MAX / 2)), nz($T::MAX - 1)); + } + + fn test_strict_pow() { + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(1).strict_pow(1), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(1), nz(3)); + assert_eq_const_safe!(NonZero<$T>: nz(3).strict_pow(2), nz(9)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_pow(0), nz(1)); + assert_eq_const_safe!(NonZero<$T>: MAX.strict_pow(1), MAX); + } + } + + #[test] + #[should_panic = "attempt to add with overflow"] + fn test_strict_add_overflow_big_small() { + let _ = MAX.strict_add(1); + } + + #[test] + #[should_panic = "attempt to add with overflow"] + fn test_strict_add_overflow_small_big() { + let _ = nz(1).strict_add($T::MAX); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_limit() { + let _ = nz($T::MAX / 2 + 1).strict_mul(nz(2)); + } + + #[test] + #[should_panic = "attempt to multiply with overflow"] + fn test_strict_mul_overflow_large() { + let _ = MAX.strict_mul(nz(2)); + } + + #[test] + #[should_panic = "attempt to exponentiate with overflow"] + fn test_strict_pow_overflow_big_base() { + let _ = MAX.strict_pow(2); + } + + #[test] + #[should_panic = "attempt to exponentiate with overflow"] + fn test_strict_pow_overflow_big_exp() { + let _ = nz(2).strict_pow(256); + } + }; +} + +mod u8 { + nonzero_uint_module!(u8); +} + +mod u16 { + nonzero_uint_module!(u16); +} + +mod u32 { + nonzero_uint_module!(u32); +} + +mod u64 { + nonzero_uint_module!(u64); +} + +mod u128 { + nonzero_uint_module!(u128); +} + +mod usize { + nonzero_uint_module!(usize); +}