From e81423abe7e2dfd2effa8cd385e9ec1376bfd9d7 Mon Sep 17 00:00:00 2001 From: HNO3Miracle Date: Wed, 24 Jun 2026 20:54:21 +0800 Subject: [PATCH 01/12] Allow either branch direction in ilog_known_base Signed-off-by: HNO3Miracle --- tests/codegen-llvm/ilog_known_base.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/codegen-llvm/ilog_known_base.rs b/tests/codegen-llvm/ilog_known_base.rs index 3f0cdd551c28b..d37e9c49ec474 100644 --- a/tests/codegen-llvm/ilog_known_base.rs +++ b/tests/codegen-llvm/ilog_known_base.rs @@ -7,7 +7,7 @@ // CHECK-LABEL: @checked_ilog2 #[no_mangle] pub fn checked_ilog2(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[LOG2:.+]] = xor i32 %[[CTZ]], 31 val.checked_ilog(2) @@ -17,7 +17,7 @@ pub fn checked_ilog2(val: u32) -> Option { // CHECK-LABEL: @checked_ilog4 #[no_mangle] pub fn checked_ilog4(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[DIV2:.+]] = lshr i32 %[[CTZ]], 1 // CHECK: %[[LOG4:.+]] = xor i32 %[[DIV2]], 15 @@ -28,9 +28,9 @@ pub fn checked_ilog4(val: u32) -> Option { // CHECK-LABEL: @checked_ilog16 #[no_mangle] pub fn checked_ilog16(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[DIV4:.+]] = lshr i32 %[[CTZ]], 2 - // CHECK: %[[LOG16:.+]] = xor i32 %[[DIV2]], 7 + // CHECK: %[[LOG16:.+]] = xor i32 %[[DIV4]], 7 val.checked_ilog(16) } From db5a6a5deb4abe8af867a7e10fdb05cc20ec165e Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Wed, 24 Jun 2026 16:28:24 +0000 Subject: [PATCH 02/12] test(diagnostics): add baseline test for macro raw pointer deref --- .../macros/deref-raw-pointer-issue-158158.rs | 21 +++++++++++++++++++ .../deref-raw-pointer-issue-158158.stderr | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/ui/macros/deref-raw-pointer-issue-158158.rs create mode 100644 tests/ui/macros/deref-raw-pointer-issue-158158.stderr diff --git a/tests/ui/macros/deref-raw-pointer-issue-158158.rs b/tests/ui/macros/deref-raw-pointer-issue-158158.rs new file mode 100644 index 0000000000000..6d45a1b78a63f --- /dev/null +++ b/tests/ui/macros/deref-raw-pointer-issue-158158.rs @@ -0,0 +1,21 @@ +struct Demo { + val: u32, +} + +macro_rules! as_ptr { + ($d:expr) => { + &mut $d as *mut Demo + }; +} + +macro_rules! get_value { + ($d:expr) => { + as_ptr!($d).val + //~^ ERROR no field `val` on type `*mut Demo` + } +} + +fn main() { + let mut d = Demo { val: 123 }; + let _ = get_value!(d); +} diff --git a/tests/ui/macros/deref-raw-pointer-issue-158158.stderr b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr new file mode 100644 index 0000000000000..842fbc984f418 --- /dev/null +++ b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr @@ -0,0 +1,19 @@ +error[E0609]: no field `val` on type `*mut Demo` + --> $DIR/deref-raw-pointer-issue-158158.rs:13:21 + | +LL | as_ptr!($d).val + | ^^^ unknown field +... +LL | let _ = get_value!(d); + | ------------- in this macro invocation + | + = note: this error originates in the macro `get_value` (in Nightly builds, run with -Z macro-backtrace for more info) +help: `$d as *mut Demo` is a raw pointer; try dereferencing it + | +LL - &mut $d as *mut Demo +LL + &mut (*). + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0609`. From 011ac7a9f8ed7d9a96ca2ee906f4948efa3e0478 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Wed, 24 Jun 2026 16:37:24 +0000 Subject: [PATCH 03/12] fix(diagnostics): suppress raw ptr deref suggestion inside macros --- compiler/rustc_hir_typeck/src/expr.rs | 3 +++ tests/ui/macros/deref-raw-pointer-issue-158158.stderr | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4465bbc34a562..d169fc7c4316f 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3159,6 +3159,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_first_deref_field(&self, err: &mut Diag<'_>, base: &hir::Expr<'_>, field: Ident) { err.span_label(field.span, "unknown field"); + if base.span.from_expansion() || field.span.from_expansion() { + return; + } let val = if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) && base.len() < 20 { diff --git a/tests/ui/macros/deref-raw-pointer-issue-158158.stderr b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr index 842fbc984f418..d93ac0eac1638 100644 --- a/tests/ui/macros/deref-raw-pointer-issue-158158.stderr +++ b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr @@ -8,11 +8,6 @@ LL | let _ = get_value!(d); | ------------- in this macro invocation | = note: this error originates in the macro `get_value` (in Nightly builds, run with -Z macro-backtrace for more info) -help: `$d as *mut Demo` is a raw pointer; try dereferencing it - | -LL - &mut $d as *mut Demo -LL + &mut (*). - | error: aborting due to 1 previous error From dad52d6986d7d2085f1aee6370fcb9176f337a4b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 25 Jun 2026 11:44:54 +0300 Subject: [PATCH 04/12] Add tests for defaults and infers in delegation's generics --- .../auxiliary/to-reuse-functions.rs | 0 tests/pretty/{ => delegation}/delegation.rs | 0 tests/pretty/delegation/generics.pp | 131 ++++++++++++++++++ tests/pretty/delegation/generics.rs | 66 +++++++++ .../impl-reuse.pp} | 2 +- .../impl-reuse.rs} | 2 +- .../inherit-attributes.pp} | 2 +- .../inherit-attributes.rs} | 2 +- .../inline-attribute.pp} | 2 +- .../inline-attribute.rs} | 2 +- .../self-rename.pp} | 2 +- .../self-rename.rs} | 2 +- .../ui/delegation/generics/infer-defaults.rs | 124 +++++++++++++++++ .../delegation/generics/infer-defaults.stderr | 70 ++++++++++ 14 files changed, 399 insertions(+), 8 deletions(-) rename tests/pretty/{ => delegation}/auxiliary/to-reuse-functions.rs (100%) rename tests/pretty/{ => delegation}/delegation.rs (100%) create mode 100644 tests/pretty/delegation/generics.pp create mode 100644 tests/pretty/delegation/generics.rs rename tests/pretty/{delegation-impl-reuse.pp => delegation/impl-reuse.pp} (94%) rename tests/pretty/{delegation-impl-reuse.rs => delegation/impl-reuse.rs} (87%) rename tests/pretty/{delegation-inherit-attributes.pp => delegation/inherit-attributes.pp} (98%) rename tests/pretty/{delegation-inherit-attributes.rs => delegation/inherit-attributes.rs} (97%) rename tests/pretty/{delegation-inline-attribute.pp => delegation/inline-attribute.pp} (98%) rename tests/pretty/{delegation-inline-attribute.rs => delegation/inline-attribute.rs} (98%) rename tests/pretty/{delegation-self-rename.pp => delegation/self-rename.pp} (97%) rename tests/pretty/{delegation-self-rename.rs => delegation/self-rename.rs} (93%) create mode 100644 tests/ui/delegation/generics/infer-defaults.rs create mode 100644 tests/ui/delegation/generics/infer-defaults.stderr diff --git a/tests/pretty/auxiliary/to-reuse-functions.rs b/tests/pretty/delegation/auxiliary/to-reuse-functions.rs similarity index 100% rename from tests/pretty/auxiliary/to-reuse-functions.rs rename to tests/pretty/delegation/auxiliary/to-reuse-functions.rs diff --git a/tests/pretty/delegation.rs b/tests/pretty/delegation/delegation.rs similarity index 100% rename from tests/pretty/delegation.rs rename to tests/pretty/delegation/delegation.rs diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp new file mode 100644 index 0000000000000..f5429c3c26a75 --- /dev/null +++ b/tests/pretty/delegation/generics.pp @@ -0,0 +1,131 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:generics.pp + +#![allow(incomplete_features)] +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn method(&self, t: (T, A, B), slice: &'_ [usize; N]) { } + fn r#static(t: (T, A, B), slice: &'_ [usize; N]) { } + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X { } + + // When infer is specified for default parameter the generic param is generated. + #[attr = Inline(Hint)] + fn foo<'a, Self, XX, Y, T, const N: _, A, B>(self: _, arg1: _, arg2: _) + -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo<'a, Self, XX, Y, T, const N: _, A, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::(arg0, arg1) + } + + // When default params are omitted they are not generated but used in signature inheritance. + #[attr = Inline(Hint)] + fn bar<'a, Self, XX, Y, A, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_bar<'a, Self, XX, Y, A, B>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + #[attr = Inline(Hint)] + fn foo1<'a, Self, XX, Y, T, const N: _, B>(self: _, arg1: _, arg2: _) -> _ + where + 'a:'a { + >::method::<(), B>(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo1<'a, Self, XX, Y, T, const N: _, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::<(), B>(arg0, arg1) + } + + // When default params are omitted they are not generated but used in signature inheritance. + #[attr = Inline(Hint)] + fn bar1<'a, Self, XX, Y, A>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_bar1<'a, Self, XX, Y, A>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + // Check with explicit self type. + #[attr = Inline(Hint)] + fn foo2<'a, XX, Y, T, const N: _, A, B>(self: _, arg1: _, arg2: _) -> _ + where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo2<'a, XX, Y, T, const N: _, A, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::(arg0, arg1) + } + + #[attr = Inline(Hint)] + fn bar2<'a, XX, Y, A, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { >::method::(self, arg1, arg2) } + #[attr = Inline(Hint)] + fn static_bar2<'a, XX, Y, A, B>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + #[attr = Inline(Hint)] + fn foo3<'a, XX, Y, T, const N: _, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::<(), B>(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo3<'a, XX, Y, T, const N: _, B>(arg0: _, arg1: _) -> _ where + 'a:'a { + >::r#static::<(), B>(arg0, arg1) + } + + #[attr = Inline(Hint)] + fn bar3<'a, XX, Y, A>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { >::method::(self, arg1, arg2) } + #[attr = Inline(Hint)] + fn static_bar3<'a, XX, Y, A>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &'_ [usize; N]) { } + fn bar(&self, t: (T, T, T), slice: &'_ [usize; N]) { } + } + + struct S; + impl Trait<'_, X, Y> for S { } + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + #[attr = Inline(Hint)] + fn foo(self: _, arg1: _, arg2: _) + -> _ { Trait::<'static, X, Y>::foo(self.0, arg1, arg2) } + #[attr = Inline(Hint)] + fn bar(self: _, arg1: _, arg2: _) + -> _ { Trait::<'static, X, Y, _, _>::foo(self.0, arg1, arg2) } + } +} + +fn main() { } diff --git a/tests/pretty/delegation/generics.rs b/tests/pretty/delegation/generics.rs new file mode 100644 index 0000000000000..7491bc663393a --- /dev/null +++ b/tests/pretty/delegation/generics.rs @@ -0,0 +1,66 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:generics.pp + +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn method(&self, t: (T, A, B), slice: &[usize; N]) {} + fn r#static(t: (T, A, B), slice: &[usize; N]) {} + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X {} + + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::method as foo; + reuse Trait::<'_, _, _, _, _>::r#static as static_foo; + + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::method as bar; + reuse Trait::<'_, _, _>::r#static as static_bar; + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::method::<(), _> as foo1; + reuse Trait::<'_, _, _, _, _>::r#static::<(), _> as static_foo1; + + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::method::<_, ()> as bar1; + reuse Trait::<'_, _, _>::r#static::<_, ()> as static_bar1; + + // Check with explicit self type. + reuse >::method as foo2; + reuse >::r#static as static_foo2; + + reuse >::method as bar2; + reuse >::r#static as static_bar2; + + reuse >::method::<(), _> as foo3; + reuse >::r#static::<(), _> as static_foo3; + + reuse >::method::<_, ()> as bar3; + reuse >::r#static::<_, ()> as static_bar3; +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S; + impl Trait<'_, X, Y> for S {} + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + reuse Trait::<'static, X, Y>::foo { self.0 } + reuse Trait::<'static, X, Y, _, _>::foo as bar { self.0 } + } +} + +fn main() {} diff --git a/tests/pretty/delegation-impl-reuse.pp b/tests/pretty/delegation/impl-reuse.pp similarity index 94% rename from tests/pretty/delegation-impl-reuse.pp rename to tests/pretty/delegation/impl-reuse.pp index 6c6c8a594fc8e..2097b23e5c524 100644 --- a/tests/pretty/delegation-impl-reuse.pp +++ b/tests/pretty/delegation/impl-reuse.pp @@ -2,7 +2,7 @@ #![no_std] //@ pretty-compare-only //@ pretty-mode:expanded -//@ pp-exact:delegation-impl-reuse.pp +//@ pp-exact:impl-reuse.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-impl-reuse.rs b/tests/pretty/delegation/impl-reuse.rs similarity index 87% rename from tests/pretty/delegation-impl-reuse.rs rename to tests/pretty/delegation/impl-reuse.rs index 9265ea18a76f0..3e813bf10d01e 100644 --- a/tests/pretty/delegation-impl-reuse.rs +++ b/tests/pretty/delegation/impl-reuse.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:expanded -//@ pp-exact:delegation-impl-reuse.pp +//@ pp-exact:impl-reuse.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp similarity index 98% rename from tests/pretty/delegation-inherit-attributes.pp rename to tests/pretty/delegation/inherit-attributes.pp index e29f76089256d..a45eb5a1c5f38 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -2,7 +2,7 @@ //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only -//@ pp-exact:delegation-inherit-attributes.pp +//@ pp-exact:inherit-attributes.pp #![allow(incomplete_features)] #![attr = Feature([fn_delegation#0])] diff --git a/tests/pretty/delegation-inherit-attributes.rs b/tests/pretty/delegation/inherit-attributes.rs similarity index 97% rename from tests/pretty/delegation-inherit-attributes.rs rename to tests/pretty/delegation/inherit-attributes.rs index 581294d472a33..538fb7cd7d75a 100644 --- a/tests/pretty/delegation-inherit-attributes.rs +++ b/tests/pretty/delegation/inherit-attributes.rs @@ -2,7 +2,7 @@ //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only -//@ pp-exact:delegation-inherit-attributes.pp +//@ pp-exact:inherit-attributes.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp similarity index 98% rename from tests/pretty/delegation-inline-attribute.pp rename to tests/pretty/delegation/inline-attribute.pp index 8cf2d98eeb412..361eb56f558f4 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp +//@ pp-exact:inline-attribute.pp #![allow(incomplete_features)] #![attr = Feature([fn_delegation#0])] diff --git a/tests/pretty/delegation-inline-attribute.rs b/tests/pretty/delegation/inline-attribute.rs similarity index 98% rename from tests/pretty/delegation-inline-attribute.rs rename to tests/pretty/delegation/inline-attribute.rs index c79f68f8942d2..f390bf64b24a7 100644 --- a/tests/pretty/delegation-inline-attribute.rs +++ b/tests/pretty/delegation/inline-attribute.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp +//@ pp-exact:inline-attribute.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-self-rename.pp b/tests/pretty/delegation/self-rename.pp similarity index 97% rename from tests/pretty/delegation-self-rename.pp rename to tests/pretty/delegation/self-rename.pp index 526021c061178..661a0ab5490eb 100644 --- a/tests/pretty/delegation-self-rename.pp +++ b/tests/pretty/delegation/self-rename.pp @@ -4,7 +4,7 @@ use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-self-rename.pp +//@ pp-exact:self-rename.pp trait Trait<'a, A, const B: bool> { diff --git a/tests/pretty/delegation-self-rename.rs b/tests/pretty/delegation/self-rename.rs similarity index 93% rename from tests/pretty/delegation-self-rename.rs rename to tests/pretty/delegation/self-rename.rs index 9054bb2b89571..c04980508cb85 100644 --- a/tests/pretty/delegation-self-rename.rs +++ b/tests/pretty/delegation/self-rename.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-self-rename.pp +//@ pp-exact:self-rename.pp #![feature(fn_delegation)] diff --git a/tests/ui/delegation/generics/infer-defaults.rs b/tests/ui/delegation/generics/infer-defaults.rs new file mode 100644 index 0000000000000..c801eab6af083 --- /dev/null +++ b/tests/ui/delegation/generics/infer-defaults.rs @@ -0,0 +1,124 @@ +#![feature(fn_delegation)] + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, A, B), slice: &[usize; N]) {} + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X {} + + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::foo as foo; + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::foo as bar; + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::foo::<(), _> as foo1; + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::foo::<_, ()> as bar1; + + // Check with explicit self type. + reuse >::foo as foo2; + reuse >::foo as bar2; + + reuse >::foo::<(), _> as foo3; + reuse >::foo::<_, ()> as bar3; + + fn check() { + foo::<'static, X, (), (), (), 1, (), ()>(&X, ((), (), ()), &[1]); + bar::<'static, X, (), (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo1::<'static, X, (), (), (), 2, ()>(&X, ((), (), ()), &[1, 2]); + bar1::<'static, X, (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo2::<'static, (), (), (), 3, (), ()>(&X, ((), (), ()), &[1, 2, 3]); + bar2::<'static, (), (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo3::<'static, (), (), (), 4, ()>(&X, ((), (), ()), &[1, 2, 3, 4]); + bar3::<'static, (), (), ()>(&X, ((), (), ()), &[1, 2]); + } +} + +mod trait_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + trait Trait2<'a, X, Y>: Trait<'a, X, Y> { + // Default params are generated as usual generics as infers are specified. + reuse Trait::<'a, X, Y, _, _>::foo; + //~^ ERROR: the trait bound `Self: trait_to_trait::Trait<'a, X, Y, T, N>` is not satisfied + + // Default params are not generated, as they are not specified. + reuse Trait::<'a, X, Y>::foo as bar; + } + + impl Trait<'static, (), ()> for () {} + impl Trait2<'static, (), ()> for () {} + + fn check() { + Trait2::<'static, (), ()>::foo::<(), 1>(&(), ((), (), ()), &[1]); + Trait2::<'static, (), ()>::bar(&(), ((), (), ()), &[1, 2]); + } +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S; + impl Trait<'_, X, Y> for S {} + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + reuse Trait::<'static, X, Y>::foo { self.0 } + reuse Trait::<'static, X, Y, _, _>::foo as bar { self.0 } + } + + fn check() { + W(S).foo(((), (), ()), &[1, 2]); + W(S).foo::<1, 2, 3>(((), (), ()), &[1, 2]); + //~^ ERROR: method takes 0 generic arguments but 3 generic arguments were supplied + + W(S).bar(((), (), ()), &[1]); + //~^ ERROR: mismatched types + W(S).bar::<((), ()), 0>(((), (), ()), &[1, 2]); + //~^ ERROR: method takes 0 generic arguments but 2 generic arguments were supplied + + W(S).bar(((), (), ()), &[1, 2]); + } +} + +mod inherent_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S(T); + + impl> S { + // Default params are not generated, as they are not specified. + reuse Trait::<'static, (), ()>::foo { self.0 } + + // Default params are generated as usual generics as infers are specified. + reuse Trait::<'static, (), (), _, _>::foo as bar { self.0 } + //~^ ERROR: the trait bound `T: inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not satisfied + } + + impl Trait<'static, (), ()> for () {} + + fn check() { + S(()).foo(((), (), ()), &[1, 2]); + S(()).bar(((), (), ()), &[1, 2]); + S(()).bar::((1, 2, 3), &[1, 2, 3, 4]); + } +} + +fn main() {} diff --git a/tests/ui/delegation/generics/infer-defaults.stderr b/tests/ui/delegation/generics/infer-defaults.stderr new file mode 100644 index 0000000000000..4d9695fc823e7 --- /dev/null +++ b/tests/ui/delegation/generics/infer-defaults.stderr @@ -0,0 +1,70 @@ +error[E0277]: the trait bound `Self: trait_to_trait::Trait<'a, X, Y, T, N>` is not satisfied + --> $DIR/infer-defaults.rs:52:40 + | +LL | reuse Trait::<'a, X, Y, _, _>::foo; + | ^^^ the trait `trait_to_trait::Trait<'a, X, Y, T, N>` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | reuse Trait::<'a, X, Y, _, _>::foo Self: trait_to_trait::Trait<'a, X, Y, T, N>; + | +++++++++++++++++++++++++++++++++++++++++++ + +error[E0107]: method takes 0 generic arguments but 3 generic arguments were supplied + --> $DIR/infer-defaults.rs:87:14 + | +LL | W(S).foo::<1, 2, 3>(((), (), ()), &[1, 2]); + | ^^^----------- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: method defined here, with 0 generic parameters + --> $DIR/infer-defaults.rs:70:12 + | +LL | fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ + +error[E0308]: mismatched types + --> $DIR/infer-defaults.rs:90:32 + | +LL | W(S).bar(((), (), ()), &[1]); + | --- ^^^^ expected an array with a size of 2, found one with a size of 1 + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/infer-defaults.rs:71:12 + | +LL | fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ ------------------ + +error[E0107]: method takes 0 generic arguments but 2 generic arguments were supplied + --> $DIR/infer-defaults.rs:92:14 + | +LL | W(S).bar::<((), ()), 0>(((), (), ()), &[1, 2]); + | ^^^--------------- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: method defined here, with 0 generic parameters + --> $DIR/infer-defaults.rs:71:12 + | +LL | fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ + +error[E0277]: the trait bound `T: inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not satisfied + --> $DIR/infer-defaults.rs:111:60 + | +LL | reuse Trait::<'static, (), (), _, _>::foo as bar { self.0 } + | --- ^^^^^^ the trait `inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not implemented for `T` + | | + | required by a bound introduced by this call + | +help: consider restricting type parameter `T` with trait `Trait` + | +LL | reuse Trait::<'static, (), (), _, _>::foo inherent_impl_to_trait::Trait<'static, (), (), T, N> as bar { self.0 } + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0107, E0277, E0308. +For more information about an error, try `rustc --explain E0107`. From 46918905875c454418f8b41bbbc982e54f8ab668 Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Thu, 18 Jun 2026 09:25:33 +0200 Subject: [PATCH 05/12] LLVM 23: Adapt codegen test to moved assume --- .../issues/issue-107681-unwrap_unchecked.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index 5834255f3d313..c594e187a0eb5 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -1,4 +1,6 @@ //@ compile-flags: -Copt-level=3 +//@ filecheck-flags: --implicit-check-not 'br {{.*}}' --implicit-check-not 'select' +//@ min-llvm-version: 22 // Test for #107681. // Make sure we don't create `br` or `select` instructions. @@ -11,10 +13,8 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK-NOT: br {{.*}} - // CHECK-NOT: select - // CHECK: [[RET:%.*]] = load i32, ptr - // CHECK-NEXT: assume - // CHECK-NEXT: ret i32 [[RET]] + // CHECK: [[INNER:%.*]] = load ptr, ptr %x + // CHECK: [[RET:%.*]] = load i32, ptr [[INNER]] + // CHECK: ret i32 [[RET]] x.next().unwrap_unchecked() } From 080c5d2d17135e7428be5a03d88a01b1ad9c4888 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 25 Jun 2026 12:17:32 +0300 Subject: [PATCH 06/12] Generate synthetic generic args only for delegation's child segment --- .../src/hir_ty_lowering/generics.rs | 11 ++++------- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 4 +++- compiler/rustc_middle/src/hir/map.rs | 7 +++++++ .../mgca/synth-gen-arg-ice-158152.rs | 11 +++++++++++ .../mgca/synth-gen-arg-ice-158152.stderr | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs create mode 100644 tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 0187a22d564cb..a36bb7bce9bab 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -6,7 +6,7 @@ use rustc_errors::{ }; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, DelegationInfo, GenericArg}; +use rustc_hir::{self as hir, GenericArg}; use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; @@ -431,15 +431,12 @@ pub(crate) fn check_generic_arg_count( } let tcx = cx.tcx(); - let parent_def = tcx.hir_get_parent_item(seg.hir_id).def_id; // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. - let explicit_late_bound = match tcx.hir_opt_delegation_info(parent_def) { - Some(DelegationInfo { child_seg_id, .. }) if seg.hir_id == *child_seg_id => { - ExplicitLateBound::No - } - _ => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), + let explicit_late_bound = match tcx.hir_is_delegation_child_segment(seg) { + true => ExplicitLateBound::No, + false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), }; let mut invalid_args = vec![]; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 6b00d58bb1e26..5af93f3f60631 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -716,6 +716,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_args: &'a GenericArgs<'tcx>, span: Span, infer_args: bool, + create_synth_args: bool, incorrect_args: &'a Result<(), GenericArgCountMismatch>, } @@ -828,7 +829,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .instantiate(tcx, preceding_args) .skip_norm_wip() .into() - } else if synthetic { + } else if self.create_synth_args && synthetic { Ty::new_param(tcx, param.index, param.name).into() } else if infer_args { self.lowerer.ty_infer(Some(param), self.span).into() @@ -868,6 +869,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, + create_synth_args: tcx.hir_is_delegation_child_segment(segment), incorrect_args: &arg_count.correct, }; diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 444433dcb6c54..3cab936c45c1f 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -879,6 +879,13 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_opt_delegation_info(delegation_id).expect("processing delegation") } + pub fn hir_is_delegation_child_segment(self, segment: &PathSegment<'_>) -> bool { + let parent_def = self.hir_get_parent_item(segment.hir_id).def_id; + + self.hir_opt_delegation_info(parent_def) + .is_some_and(|info| info.child_seg_id == segment.hir_id) + } + #[inline] fn hir_opt_ident(self, id: HirId) -> Option { match self.hir_node(id) { diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs new file mode 100644 index 0000000000000..11d970534f513 --- /dev/null +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +trait A {} +trait Trait {} + +impl A<[usize; fn_item]> for () {} + +fn fn_item(_: impl Trait) {} +//~^ ERROR: type provided when a constant was expected + +fn main() {} diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr new file mode 100644 index 0000000000000..0cb59587ee20b --- /dev/null +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr @@ -0,0 +1,14 @@ +error[E0747]: type provided when a constant was expected + --> $DIR/synth-gen-arg-ice-158152.rs:8:26 + | +LL | fn fn_item(_: impl Trait) {} + | ^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | fn fn_item(_: impl Trait<{ usize }>) {} + | + + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0747`. From a2bb1913be47bbac4048769227b81639b11583be Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Jun 2026 13:59:41 +0200 Subject: [PATCH 07/12] add `TyAndLayout::uninit_ranges` --- compiler/rustc_abi/src/layout/ty.rs | 132 +++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index b09afc9ec8af6..5e92ef03dba40 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -1,5 +1,5 @@ use std::fmt; -use std::ops::Deref; +use std::ops::{Deref, Range}; use rustc_data_structures::intern::Interned; use rustc_macros::StableHash; @@ -282,4 +282,134 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } found } + + pub fn uninit_ranges(&self, cx: &C) -> Vec> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let mut data = RangeSet(Vec::new()); + self.add_data_ranges(cx, Size::ZERO, &mut data); + + // Find gaps between the data ranges. + let mut uninit_ranges = Vec::new(); + let mut covered_until = Size::ZERO; + for &(offset, size) in data.0.iter() { + if offset > covered_until { + uninit_ranges.push(covered_until..offset); + } + covered_until = Ord::max(covered_until, offset + size); + } + + // Add trailing padding. + if self.size > covered_until { + uninit_ranges.push(covered_until..self.size); + } + + uninit_ranges + } + + /// Ranges of bytes that are initialized for some valid value of this type. In particular for + /// enums and unions there are offsets that are initialized for some variants but not for + /// others. + fn add_data_ranges(self, cx: &C, base_offset: Size, out: &mut RangeSet) + where + Ty: TyAbiInterface<'a, C> + Copy, + { + if self.is_zst() { + return; + } + + match &self.variants { + Variants::Empty => { /* done */ } + Variants::Single { index: _ } => match &self.fields { + FieldsShape::Primitive => { + out.add_range(base_offset, self.size); + } + &FieldsShape::Union(field_count) => { + for field in 0..field_count.get() { + let field = self.field(cx, field); + field.add_data_ranges(cx, base_offset, out); + } + } + &FieldsShape::Array { stride, count } => { + let elem = self.field(cx, 0); + + // For scalars we know there is no padding between the elements, + // so the entire array is a single big data range. + if elem.backend_repr.is_scalar() { + out.add_range(base_offset, elem.size * count); + } else { + // FIXME: this is really inefficient for large arrays. + for idx in 0..count { + elem.add_data_ranges(cx, base_offset + idx * stride, out); + } + } + } + FieldsShape::Arbitrary { offsets, in_memory_order: _ } => { + for (field, &offset) in offsets.iter_enumerated() { + let field = self.field(cx, field.as_usize()); + field.add_data_ranges(cx, base_offset + offset, out); + } + } + }, + Variants::Multiple { variants, .. } => { + for variant in variants.indices() { + let variant = self.for_variant(cx, variant); + variant.add_data_ranges(cx, base_offset, out); + } + } + } + } +} + +// FIXME: dedup with the one in +// `compiler/rustc_const_eval/src/interpret/validity.rs` +/// Represents a set of `Size` values as a sorted list of ranges. +// These are (offset, length) pairs, and they are sorted and mutually disjoint, +// and never adjacent (i.e. there's always a gap between two of them). +#[derive(Debug, Clone)] +struct RangeSet(Vec<(Size, Size)>); + +impl RangeSet { + fn add_range(&mut self, offset: Size, size: Size) { + if size.bytes() == 0 { + // No need to track empty ranges. + return; + } + let v = &mut self.0; + // We scan for a partition point where the left partition is all the elements that end + // strictly before we start. Those are elements that are too "low" to merge with us. + let idx = + v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); + // Now we want to either merge with the first element of the second partition, or insert ourselves before that. + if let Some(&(other_offset, other_size)) = v.get(idx) + && offset + size >= other_offset + { + // Their end is >= our start (otherwise it would not be in the 2nd partition) and + // our end is >= their start. This means we can merge the ranges. + let new_start = other_offset.min(offset); + let mut new_end = (other_offset + other_size).max(offset + size); + // We grew to the right, so merge with overlapping/adjacent elements. + // (We also may have grown to the left, but that can never make us adjacent with + // anything there since we selected the first such candidate via `partition_point`.) + let mut scan_right = 1; + while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) + && new_end >= next_offset + { + // Increase our size to absorb the next element. + new_end = new_end.max(next_offset + next_size); + // Look at the next element. + scan_right += 1; + } + // Update the element we grew. + v[idx] = (new_start, new_end - new_start); + // Remove the elements we absorbed (if any). + if scan_right > 1 { + drop(v.drain((idx + 1)..(idx + scan_right))); + } + } else { + // Insert new element. + v.insert(idx, (offset, size)); + } + } } From 3cfdde8d9b88512b590eaf77657f7835ee79faa4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Jun 2026 17:29:52 +0200 Subject: [PATCH 08/12] zero out padding in cmse entry return values --- compiler/rustc_codegen_ssa/src/mir/block.rs | 39 ++++++- tests/assembly-llvm/cmse-clear-padding.rs | 119 ++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 tests/assembly-llvm/cmse-clear-padding.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b31f785c22234..e6498af25d4bc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,8 @@ use std::cmp; -use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange}; +use rustc_abi::{ + Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Reg, Size, WrappingRange, +}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; @@ -545,6 +547,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("C-variadic function must have a `VaList` place"), } } + if self.fn_abi.ret.layout.is_uninhabited() { // Functions with uninhabited return values are marked `noreturn`, // so we should make sure that we never actually do. @@ -556,6 +559,40 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); return; } + + if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) + && let PassMode::Cast { cast, .. } = &self.fn_abi.ret.mode + { + // The return value of an `extern "cmse-nonsecure-entry"` function crosses the secure + // boundary. Ensure that stale information in padding or otherwise uninitialized memory + // does not leak across the secure boundary. + // + // We zero all bytes that are statically know to be uninitialized, we do not inspect the + // runtime value. + let ret_layout = self.fn_abi.ret.layout; + let uninit_ranges = ret_layout.uninit_ranges(bx.cx()); + if !uninit_ranges.is_empty() { + // Materialize the return value. + let tmp = PlaceRef::alloca(bx, ret_layout); + let op = self.codegen_consume(bx, mir::Place::return_place().as_ref()); + op.val.store(bx, tmp); + + let zero = bx.const_u8(0); + for range in uninit_ranges { + let len = bx.const_usize((range.end - range.start).bytes()); + let offset = bx.const_usize(range.start.bytes()); + + let ptr = bx.inbounds_ptradd(tmp.val.llval, offset); + bx.memset(ptr, zero, len, Align::ONE, MemFlags::empty()); + } + + // Load the value back and return. + let llval = load_cast(bx, cast, tmp.val.llval, ret_layout.align.abi); + bx.ret(llval); + return; + } + } + let llval = match &self.fn_abi.ret.mode { PassMode::Ignore | PassMode::Indirect { .. } => { bx.ret_void(); diff --git a/tests/assembly-llvm/cmse-clear-padding.rs b/tests/assembly-llvm/cmse-clear-padding.rs new file mode 100644 index 0000000000000..28be248027ba5 --- /dev/null +++ b/tests/assembly-llvm/cmse-clear-padding.rs @@ -0,0 +1,119 @@ +//@ add-minicore +//@ min-llvm-version: 22 +//@ assembly-output: emit-asm +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Copt-level=1 +//@ needs-llvm-components: arm +#![crate_type = "lib"] +#![feature(abi_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)] +#![no_core] + +// Test that padding and other uninitialized bytes are zeroed when a value crosses the secure +// boundary. +// +// The assembly uses the following instructions for clearing the bits: +// +// - `uxtb` clears bits 8..32 +// - `uxth` clears bits 16..32 +// - `bic` clears bits based on a mask + +extern crate minicore; +use minicore::*; + +#[repr(C)] +pub struct InnerPadding { + a: u8, + b: u16, +} + +// CHECK-LABEL: c_ret_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "C" fn c_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { + InnerPadding { a, b } +} + +// CHECK-LABEL: cmse_ret_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "cmse-nonsecure-entry" fn cmse_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { + InnerPadding { a, b } +} + +#[repr(C)] +pub struct TrailingPadding { + a: u16, + b: u8, +} + +// CHECK-LABEL: c_ret_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "C" fn c_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { + TrailingPadding { a, b } +} + +// CHECK-LABEL: cmse_ret_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r1, r1 +// CHECK-NEXT: uxth r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "cmse-nonsecure-entry" fn cmse_ret_with_trailing_padding( + a: u16, + b: u8, +) -> TrailingPadding { + TrailingPadding { a, b } +} + +#[repr(C, align(2))] +pub struct WideU8 { + a: u8, +} + +// CHECK-LABEL: c_ret_with_wide_u8: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { + [WideU8 { a }, WideU8 { a: b }] +} + +// CHECK-LABEL: cmse_ret_with_wide_u8: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r1, r1 +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { + [WideU8 { a }, WideU8 { a: b }] +} + +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( + a: u16, + b: u16, +) -> [MaybeUninit; 2] { + unsafe { [mem::transmute(a), mem::transmute(b)] } +} + +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit_tuple: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( + a: u16, + b: u16, +) -> (MaybeUninit, MaybeUninit) { + unsafe { (mem::transmute(a), mem::transmute(b)) } +} From 6ce8f07da0685f389bb4de75ce8abbbcce02e2eb Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Jun 2026 19:28:21 +0200 Subject: [PATCH 09/12] zero out padding in cmse call arguments --- compiler/rustc_codegen_ssa/src/mir/block.rs | 97 +++++++++++------ tests/assembly-llvm/cmse-clear-padding.rs | 113 +++++++++++++++++--- 2 files changed, 162 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e6498af25d4bc..a70f966d2e63c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,4 +1,5 @@ use std::cmp; +use std::ops::Range; use rustc_abi::{ Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Reg, Size, WrappingRange, @@ -560,39 +561,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) - && let PassMode::Cast { cast, .. } = &self.fn_abi.ret.mode - { - // The return value of an `extern "cmse-nonsecure-entry"` function crosses the secure - // boundary. Ensure that stale information in padding or otherwise uninitialized memory - // does not leak across the secure boundary. - // - // We zero all bytes that are statically know to be uninitialized, we do not inspect the - // runtime value. - let ret_layout = self.fn_abi.ret.layout; - let uninit_ranges = ret_layout.uninit_ranges(bx.cx()); - if !uninit_ranges.is_empty() { - // Materialize the return value. - let tmp = PlaceRef::alloca(bx, ret_layout); - let op = self.codegen_consume(bx, mir::Place::return_place().as_ref()); - op.val.store(bx, tmp); - - let zero = bx.const_u8(0); - for range in uninit_ranges { - let len = bx.const_usize((range.end - range.start).bytes()); - let offset = bx.const_usize(range.start.bytes()); - - let ptr = bx.inbounds_ptradd(tmp.val.llval, offset); - bx.memset(ptr, zero, len, Align::ONE, MemFlags::empty()); - } - - // Load the value back and return. - let llval = load_cast(bx, cast, tmp.val.llval, ret_layout.align.abi); - bx.ret(llval); - return; - } - } - let llval = match &self.fn_abi.ret.mode { PassMode::Ignore | PassMode::Indirect { .. } => { bx.ret_void(); @@ -634,6 +602,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; + + if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { + // The return value of an `extern "cmse-nonsecure-entry"` function crosses the secure + // boundary. Zero padding and uninitialized bytes so information does not leak. + let ret_layout = self.fn_abi.ret.layout; + let uninit_ranges = ret_layout.uninit_ranges(bx.cx()); + self.zero_byte_ranges(bx, llslot, ret_layout.size, &uninit_ranges); + } + load_cast(bx, cast_ty, llslot, self.fn_abi.ret.layout.align.abi) } }; @@ -1378,6 +1355,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_argument( bx, + fn_abi.conv, op, by_move, &mut llargs, @@ -1388,6 +1366,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let num_untupled = untuple.map(|tup| { self.codegen_arguments_untupled( bx, + fn_abi.conv, &tup.node, &mut llargs, &fn_abi.args[first_args.len()..], @@ -1417,6 +1396,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let last_arg = fn_abi.args.last().unwrap(); self.codegen_argument( bx, + fn_abi.conv, location, /* by_move */ false, &mut llargs, @@ -1733,9 +1713,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + fn zero_byte_ranges( + &mut self, + bx: &mut Bx, + ptr: Bx::Value, + limit: Size, + ranges: &[Range], + ) { + let zero = bx.const_u8(0); + + for range in ranges { + let end = cmp::min(range.end, limit); + if range.start >= end { + continue; + } + let offset = bx.const_usize(range.start.bytes()); + let len = bx.const_usize((end - range.start).bytes()); + let ptr = bx.inbounds_ptradd(ptr, offset); + bx.memset(ptr, zero, len, Align::ONE, MemFlags::empty()); + } + } + fn codegen_argument( &mut self, bx: &mut Bx, + conv: CanonAbi, op: OperandRef<'tcx, Bx::Value>, by_move: bool, llargs: &mut Vec, @@ -1859,6 +1861,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MemFlags::empty(), None, ); + + // The arguments of an `extern "cmse-nonsecure-call"` function cross the secure + // boundary. Zero padding bytes so information does not leak. + // + // This only zeroes "guaranteed" padding. There may be more bytes that are + // padding for some but not all variants of this type; those are not zeroed. + // + // Passing an argument with value-dependent padding will instead trigger a lint. + if conv == CanonAbi::Arm(ArmCall::CCmseNonSecureCall) { + self.zero_byte_ranges( + bx, + llscratch, + Size::from_bytes(copy_bytes), + &arg.layout.uninit_ranges(bx.cx()), + ); + } + // ...and then load it with the ABI type. llval = load_cast(bx, cast, llscratch, scratch_align); bx.lifetime_end(llscratch, scratch_size); @@ -1885,6 +1904,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_arguments_untupled( &mut self, bx: &mut Bx, + conv: CanonAbi, operand: &mir::Operand<'tcx>, llargs: &mut Vec, args: &[ArgAbi<'tcx, Ty<'tcx>>], @@ -1904,6 +1924,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let field = bx.load_operand(field_ptr); self.codegen_argument( bx, + conv, field, by_move, llargs, @@ -1915,7 +1936,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // If the tuple is immediate, the elements are as well. for i in 0..tuple.layout.fields.count() { let op = tuple.extract_field(self, bx, i); - self.codegen_argument(bx, op, by_move, llargs, &args[i], lifetime_ends_after_call); + self.codegen_argument( + bx, + conv, + op, + by_move, + llargs, + &args[i], + lifetime_ends_after_call, + ); } } tuple.layout.fields.count() diff --git a/tests/assembly-llvm/cmse-clear-padding.rs b/tests/assembly-llvm/cmse-clear-padding.rs index 28be248027ba5..b092bf304dcca 100644 --- a/tests/assembly-llvm/cmse-clear-padding.rs +++ b/tests/assembly-llvm/cmse-clear-padding.rs @@ -15,12 +15,15 @@ // - `uxtb` clears bits 8..32 // - `uxth` clears bits 16..32 // - `bic` clears bits based on a mask +// +// When passing arguments the current implementation of the C ABI already clears padding sometimes, +// but it's not something that can be relied on. extern crate minicore; use minicore::*; #[repr(C)] -pub struct InnerPadding { +struct InnerPadding { a: u8, b: u16, } @@ -29,7 +32,7 @@ pub struct InnerPadding { // CHECK: mov r7, sp // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "C" fn c_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { +extern "C" fn c_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { InnerPadding { a, b } } @@ -38,12 +41,35 @@ pub extern "C" fn c_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { // CHECK-NEXT: uxtb r0, r0 // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "cmse-nonsecure-entry" fn cmse_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { +extern "cmse-nonsecure-entry" fn cmse_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { InnerPadding { a, b } } +// CHECK-LABEL: c_call_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #65280 +// CHECK-NEXT: pop.w {r7, lr} +#[no_mangle] +extern "C" fn c_call_with_inner_padding(f: unsafe extern "C" fn(InnerPadding), x: InnerPadding) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #65280 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_inner_padding( + f: unsafe extern "cmse-nonsecure-call" fn(InnerPadding), + x: InnerPadding, +) { + unsafe { f(x) } +} + #[repr(C)] -pub struct TrailingPadding { +struct TrailingPadding { a: u16, b: u8, } @@ -52,7 +78,7 @@ pub struct TrailingPadding { // CHECK: mov r7, sp // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "C" fn c_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { +extern "C" fn c_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { TrailingPadding { a, b } } @@ -62,15 +88,38 @@ pub extern "C" fn c_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding // CHECK-NEXT: uxth r0, r0 // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "cmse-nonsecure-entry" fn cmse_ret_with_trailing_padding( - a: u16, - b: u8, -) -> TrailingPadding { +extern "cmse-nonsecure-entry" fn cmse_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { TrailingPadding { a, b } } +// CHECK-LABEL: c_call_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #-16777216 +// CHECK-NEXT: pop.w {r7, lr} +#[no_mangle] +extern "C" fn c_call_with_trailing_padding( + f: unsafe extern "C" fn(TrailingPadding), + x: TrailingPadding, +) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #-16777216 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_trailing_padding( + f: unsafe extern "cmse-nonsecure-call" fn(TrailingPadding), + x: TrailingPadding, +) { + unsafe { f(x) } +} + #[repr(C, align(2))] -pub struct WideU8 { +struct WideU8 { a: u8, } @@ -78,7 +127,7 @@ pub struct WideU8 { // CHECK: mov r7, sp // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { +extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { [WideU8 { a }, WideU8 { a: b }] } @@ -88,17 +137,53 @@ pub extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { // CHECK-NEXT: uxtb r0, r0 // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 #[no_mangle] -pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { [WideU8 { a }, WideU8 { a: b }] } +// CHECK-LABEL: c_call_with_inner_wide_u8: +// CHECK: push {r7, lr} +// CHECK-NEXT: .setfp r7, sp +// CHECK-NEXT: mov r7, sp +// CHECK-NEXT: mov lr, r3 +// CHECK-NEXT: mov r12, r0 +// CHECK-NEXT: ldr r3, [r7, #8] +// CHECK-NEXT: mov r0, r1 +// CHECK-NEXT: mov r1, r2 +// CHECK-NEXT: mov r2, lr +// CHECK-NEXT: pop.w {r7, lr} +// CHECK-NEXT: bx r12 +#[no_mangle] +extern "C" fn c_call_with_inner_wide_u8(f: unsafe extern "C" fn([WideU8; 8]), x: [WideU8; 8]) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_inner_wide_u8: +// CHECK: push {r7, lr} +// CHECK-NEXT: .setfp r7, sp +// CHECK-NEXT: mov r7, sp +// CHECK-NEXT: mov r12, r0 +// CHECK-NEXT: bic r0, r1, #-16711936 +// CHECK-NEXT: bic r1, r2, #-16711936 +// CHECK-NEXT: bic r2, r3, #-16711936 +// CHECK-NEXT: ldr r3, [r7, #8] +// CHECK-NEXT: bic r3, r3, #-16711936 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_inner_wide_u8( + f: unsafe extern "cmse-nonsecure-call" fn([WideU8; 8]), + x: [WideU8; 8], +) { + unsafe { f(x) } +} + // CHECK-LABEL: cmse_ret_with_wide_u8_uninit: // CHECK: mov r7, sp // CHECK-NEXT: uxtb r0, r0 // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 // CHECK-NEXT: bic r0, r0, #-16711936 #[no_mangle] -pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( a: u16, b: u16, ) -> [MaybeUninit; 2] { @@ -111,7 +196,7 @@ pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( // CHECK-NEXT: orr.w r0, r0, r1, lsl #16 // CHECK-NEXT: bic r0, r0, #-16711936 #[no_mangle] -pub extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( a: u16, b: u16, ) -> (MaybeUninit, MaybeUninit) { From 5f34c5074ac019ff64095926500b6430d3877cef Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 25 Jun 2026 13:15:03 +0200 Subject: [PATCH 10/12] deduplicate `RangeSet` --- compiler/rustc_abi/src/layout/ty.rs | 72 ++++--------------- compiler/rustc_abi/src/lib.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 15 ++-- .../src/interpret/validity.rs | 54 +------------- compiler/rustc_data_structures/src/lib.rs | 1 + .../rustc_data_structures/src/range_set.rs | 59 +++++++++++++++ 6 files changed, 87 insertions(+), 116 deletions(-) create mode 100644 compiler/rustc_data_structures/src/range_set.rs diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 5e92ef03dba40..9ca4aa2254547 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -2,6 +2,7 @@ use std::fmt; use std::ops::{Deref, Range}; use rustc_data_structures::intern::Interned; +use rustc_data_structures::range_set::RangeSet; use rustc_macros::StableHash; use crate::layout::{FieldIdx, VariantIdx}; @@ -283,11 +284,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { found } - pub fn uninit_ranges(&self, cx: &C) -> Vec> + /// The ranges of bytes that are always ignored by the representation relation of this type. + /// + /// In other words, for any sequence of bytes, if we reset the these padding bytes to uninit, + /// then these two sequences of bytes represent the same value (or they are both invalid). + /// This is the "guaranteed" padding. There may be more bytes that are padding for some + /// but not all variants of this type; those are not included. + /// (E.g. `Option` has no guaranteed padding so the empty range set is returned, but its `None` value still has padding). + pub fn padding_ranges(&self, cx: &C) -> Vec> where Ty: TyAbiInterface<'a, C> + Copy, { - let mut data = RangeSet(Vec::new()); + let mut data = RangeSet::new(); self.add_data_ranges(cx, Size::ZERO, &mut data); // Find gaps between the data ranges. @@ -308,10 +316,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { uninit_ranges } - /// Ranges of bytes that are initialized for some valid value of this type. In particular for - /// enums and unions there are offsets that are initialized for some variants but not for - /// others. - fn add_data_ranges(self, cx: &C, base_offset: Size, out: &mut RangeSet) + /// Extend `out` with all ranges of bytes that *may* carry relevant data for values of this type. + /// For enums and unions there are offsets that are initialized for some + /// variants but not for others; those offset *will* get added to `out`. + fn add_data_ranges(self, cx: &C, base_offset: Size, out: &mut RangeSet) where Ty: TyAbiInterface<'a, C> + Copy, { @@ -361,55 +369,3 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } } } - -// FIXME: dedup with the one in -// `compiler/rustc_const_eval/src/interpret/validity.rs` -/// Represents a set of `Size` values as a sorted list of ranges. -// These are (offset, length) pairs, and they are sorted and mutually disjoint, -// and never adjacent (i.e. there's always a gap between two of them). -#[derive(Debug, Clone)] -struct RangeSet(Vec<(Size, Size)>); - -impl RangeSet { - fn add_range(&mut self, offset: Size, size: Size) { - if size.bytes() == 0 { - // No need to track empty ranges. - return; - } - let v = &mut self.0; - // We scan for a partition point where the left partition is all the elements that end - // strictly before we start. Those are elements that are too "low" to merge with us. - let idx = - v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); - // Now we want to either merge with the first element of the second partition, or insert ourselves before that. - if let Some(&(other_offset, other_size)) = v.get(idx) - && offset + size >= other_offset - { - // Their end is >= our start (otherwise it would not be in the 2nd partition) and - // our end is >= their start. This means we can merge the ranges. - let new_start = other_offset.min(offset); - let mut new_end = (other_offset + other_size).max(offset + size); - // We grew to the right, so merge with overlapping/adjacent elements. - // (We also may have grown to the left, but that can never make us adjacent with - // anything there since we selected the first such candidate via `partition_point`.) - let mut scan_right = 1; - while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) - && new_end >= next_offset - { - // Increase our size to absorb the next element. - new_end = new_end.max(next_offset + next_size); - // Look at the next element. - scan_right += 1; - } - // Update the element we grew. - v[idx] = (new_start, new_end - new_start); - // Remove the elements we absorbed (if any). - if scan_right > 1 { - drop(v.drain((idx + 1)..(idx + scan_right))); - } - } else { - // Insert new element. - v.insert(idx, (offset, size)); - } - } -} diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 166c8bea6f354..a5a4411146c44 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -792,7 +792,7 @@ impl FromStr for Endian { } /// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))] pub struct Size { raw: u64, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index a70f966d2e63c..115c50edf4e9f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -548,7 +548,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("C-variadic function must have a `VaList` place"), } } - if self.fn_abi.ret.layout.is_uninhabited() { // Functions with uninhabited return values are marked `noreturn`, // so we should make sure that we never actually do. @@ -560,7 +559,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); return; } - let llval = match &self.fn_abi.ret.mode { PassMode::Ignore | PassMode::Indirect { .. } => { bx.ret_void(); @@ -604,10 +602,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { - // The return value of an `extern "cmse-nonsecure-entry"` function crosses the secure - // boundary. Zero padding and uninitialized bytes so information does not leak. + // The return value of an `extern "cmse-nonsecure-entry"` function crosses the + // secure boundary. Zero padding bytes so information does not leak. + // + // This only zeroes "guaranteed" padding. There may be more bytes that are + // padding for some but not all variants of this type; those are not zeroed. + // + // Returning a value with value-dependent padding will instead trigger a lint. let ret_layout = self.fn_abi.ret.layout; - let uninit_ranges = ret_layout.uninit_ranges(bx.cx()); + let uninit_ranges = ret_layout.padding_ranges(bx.cx()); self.zero_byte_ranges(bx, llslot, ret_layout.size, &uninit_ranges); } @@ -1874,7 +1877,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx, llscratch, Size::from_bytes(copy_bytes), - &arg.layout.uninit_ranges(bx.cx()), + &arg.layout.padding_ranges(bx.cx()), ); } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 224337e68cfbf..262ef6ba74ed0 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -345,55 +345,7 @@ fn write_path(out: &mut String, path: &[PathElem<'_>]) { } } -/// Represents a set of `Size` values as a sorted list of ranges. -// These are (offset, length) pairs, and they are sorted and mutually disjoint, -// and never adjacent (i.e. there's always a gap between two of them). -#[derive(Debug, Clone)] -pub struct RangeSet(Vec<(Size, Size)>); - -impl RangeSet { - fn add_range(&mut self, offset: Size, size: Size) { - if size.bytes() == 0 { - // No need to track empty ranges. - return; - } - let v = &mut self.0; - // We scan for a partition point where the left partition is all the elements that end - // strictly before we start. Those are elements that are too "low" to merge with us. - let idx = - v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); - // Now we want to either merge with the first element of the second partition, or insert ourselves before that. - if let Some(&(other_offset, other_size)) = v.get(idx) - && offset + size >= other_offset - { - // Their end is >= our start (otherwise it would not be in the 2nd partition) and - // our end is >= their start. This means we can merge the ranges. - let new_start = other_offset.min(offset); - let mut new_end = (other_offset + other_size).max(offset + size); - // We grew to the right, so merge with overlapping/adjacent elements. - // (We also may have grown to the left, but that can never make us adjacent with - // anything there since we selected the first such candidate via `partition_point`.) - let mut scan_right = 1; - while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) - && new_end >= next_offset - { - // Increase our size to absorb the next element. - new_end = new_end.max(next_offset + next_size); - // Look at the next element. - scan_right += 1; - } - // Update the element we grew. - v[idx] = (new_start, new_end - new_start); - // Remove the elements we absorbed (if any). - if scan_right > 1 { - drop(v.drain((idx + 1)..(idx + scan_right))); - } - } else { - // Insert new element. - v.insert(idx, (offset, size)); - } - } -} +pub type RangeSet = rustc_data_structures::range_set::RangeSet; struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> { /// The `path` may be pushed to, but the part that is present when a function @@ -1194,7 +1146,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { assert!(layout.is_sized(), "there are no unsized unions"); let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env); return M::cached_union_data_range(ecx, layout.ty, || { - let mut out = RangeSet(Vec::new()); + let mut out = RangeSet::new(); union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out); out }); @@ -1644,7 +1596,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ctfe_mode, ecx, reset_provenance_and_padding, - data_bytes: reset_padding.then_some(RangeSet(Vec::new())), + data_bytes: reset_padding.then_some(RangeSet::new()), may_dangle: start_in_may_dangle, }; v.visit_value(val)?; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index be8538acd30eb..73b0dbd1ceeba 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -68,6 +68,7 @@ pub mod obligation_forest; pub mod owned_slice; pub mod packed; pub mod profiling; +pub mod range_set; pub mod sharded; pub mod small_c_str; pub mod snapshot_map; diff --git a/compiler/rustc_data_structures/src/range_set.rs b/compiler/rustc_data_structures/src/range_set.rs new file mode 100644 index 0000000000000..514946a0fb2de --- /dev/null +++ b/compiler/rustc_data_structures/src/range_set.rs @@ -0,0 +1,59 @@ +/// Represents a set of `Size` values as a sorted list of ranges. +/// +/// These are (offset, length) pairs, and they are sorted and mutually disjoint, +/// and never adjacent (i.e. there's always a gap between two of them). +#[derive(Debug, Clone)] +pub struct RangeSet(pub Vec<(T, T)>); + +impl RangeSet +where + T: Copy + Ord + Default, + T: core::ops::Add, + T: core::ops::Sub, +{ + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add_range(&mut self, offset: T, size: T) { + if size == T::default() { + // No need to track empty ranges. + return; + } + let v = &mut self.0; + // We scan for a partition point where the left partition is all the elements that end + // strictly before we start. Those are elements that are too "low" to merge with us. + let idx = + v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); + // Now we want to either merge with the first element of the second partition, or insert ourselves before that. + if let Some(&(other_offset, other_size)) = v.get(idx) + && offset + size >= other_offset + { + // Their end is >= our start (otherwise it would not be in the 2nd partition) and + // our end is >= their start. This means we can merge the ranges. + let new_start = other_offset.min(offset); + let mut new_end = (other_offset + other_size).max(offset + size); + // We grew to the right, so merge with overlapping/adjacent elements. + // (We also may have grown to the left, but that can never make us adjacent with + // anything there since we selected the first such candidate via `partition_point`.) + let mut scan_right = 1; + while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) + && new_end >= next_offset + { + // Increase our size to absorb the next element. + new_end = new_end.max(next_offset + next_size); + // Look at the next element. + scan_right += 1; + } + // Update the element we grew. + v[idx] = (new_start, new_end - new_start); + // Remove the elements we absorbed (if any). + if scan_right > 1 { + drop(v.drain((idx + 1)..(idx + scan_right))); + } + } else { + // Insert new element. + v.insert(idx, (offset, size)); + } + } +} From 42e329173179ed65b07e37f0f05b4407d177925e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 22 Jun 2026 17:27:14 +0200 Subject: [PATCH 11/12] Move part of the target checking for `#[may_dangle]` to the attribute parser --- .../src/attributes/semantics.rs | 8 +++- .../rustc_attr_parsing/src/target_checking.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 23 ++++++---- tests/ui/attributes/may_dangle.rs | 10 ++--- tests/ui/attributes/may_dangle.stderr | 44 ++++++++++++------- 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index 74b2cc2ffd5a1..cdcb8e95da726 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -1,11 +1,17 @@ use rustc_feature::AttributeStability; +use rustc_hir::target::GenericParamKind; use super::prelude::*; pub(crate) struct MayDangleParser; impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: true }), + ]); const STABILITY: AttributeStability = unstable!(dropck_eyepatch); const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle; } diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 96737be04f5b2..03100381d23f3 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -416,6 +416,7 @@ pub(crate) fn allowed_targets_applied( // ensure a consistent order target_strings.sort(); + target_strings.dedup(); // If there is now only 1 target left, show that as the only possible target let only_target = target_strings.len() == 1; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 079fdd923ac0d..701f39e7c0e39 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -24,8 +24,9 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, - Item, ItemKind, MethodKind, Node, ParamName, Target, TraitItem, find_attr, + self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParam, + GenericParamKind, HirId, Item, ItemKind, MethodKind, Node, ParamName, Target, TraitItem, + find_attr, }; use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; @@ -1121,12 +1122,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl. fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) { - if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id) - && matches!( - param.kind, - hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } - ) - && matches!(param.source, hir::GenericParamSource::Generics) + let hir::Node::GenericParam( + param @ GenericParam { + kind: hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }, + .. + }, + ) = self.tcx.hir_node(hir_id) + else { + self.dcx().delayed_bug("Checked in attr parser"); + return; + }; + + if matches!(param.source, hir::GenericParamSource::Generics) && let parent_hir_id = self.tcx.parent_hir_id(hir_id) && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id) && let hir::ItemKind::Impl(impl_) = item.kind diff --git a/tests/ui/attributes/may_dangle.rs b/tests/ui/attributes/may_dangle.rs index 209ba0e88ad18..401efce88851c 100644 --- a/tests/ui/attributes/may_dangle.rs +++ b/tests/ui/attributes/may_dangle.rs @@ -12,7 +12,7 @@ unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> { //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> { - //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl + //~^ ERROR attribute cannot be used on fn drop(&mut self) {} } @@ -39,15 +39,15 @@ mod fake { } } -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on struct Dangling; -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on impl NotDrop for () { } -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on fn main() { - #[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl + #[may_dangle] //~ ERROR attribute cannot be used on let () = (); } diff --git a/tests/ui/attributes/may_dangle.stderr b/tests/ui/attributes/may_dangle.stderr index dc24f847f712f..596c44aa958e1 100644 --- a/tests/ui/attributes/may_dangle.stderr +++ b/tests/ui/attributes/may_dangle.stderr @@ -1,44 +1,54 @@ -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl - --> $DIR/may_dangle.rs:8:13 - | -LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {} - | ^^^^^^^^^^^^^ - -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl - --> $DIR/may_dangle.rs:11:17 - | -LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {} - | ^^^^^^^^^^^^^ - -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on const parameters --> $DIR/may_dangle.rs:14:20 | LL | unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> { | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on structs --> $DIR/may_dangle.rs:42:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on trait impl blocks --> $DIR/may_dangle.rs:45:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on functions --> $DIR/may_dangle.rs:49:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on statements --> $DIR/may_dangle.rs:51:5 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters + +error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl + --> $DIR/may_dangle.rs:8:13 + | +LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {} + | ^^^^^^^^^^^^^ + +error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl + --> $DIR/may_dangle.rs:11:17 + | +LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {} + | ^^^^^^^^^^^^^ error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl --> $DIR/may_dangle.rs:36:17 From b442414fbd60d4f61487ef12062b6b75d21ccbf5 Mon Sep 17 00:00:00 2001 From: Sungbin Jo Date: Fri, 26 Jun 2026 01:04:45 +0900 Subject: [PATCH 12/12] Update LLVM for Mach-O __LINKEDIT alignment fix. --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index ec9ab9d68bf7a..3c3f13025bf9f 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit ec9ab9d68bf7a0e86b2ddf3c0e6e3c4620e02961 +Subproject commit 3c3f13025bf9f99bb2a757eb37dad4f09e7d6c36