From 8be779bb3500a79ec6774a968f23771e1aba7263 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Oct 2026 08:01:29 +1100 Subject: [PATCH 1/6] Replace `match_span_kind` macro with `SpanKind` The macro was written to get around some suboptimal code generation. After the recent span compression overhaul it is no longer needed, and we can use a simpler type to achieve the same goal. --- compiler/rustc_span/src/span_encoding.rs | 125 +++++++++++------------ 1 file changed, 57 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 3947b9095f0c2..1b8569b5a7fde 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -352,32 +352,13 @@ impl Interned { } const _: () = Interned::INDEX.assert_is_bottom(); -// This code is very hot, and converting span to an enum and matching on it doesn't optimize away -// properly. So we are using a macro emulating such a match, but expand it directly to an if-else -// chain. -macro_rules! match_span_kind { - ( - $span:expr, - InlineCtxt($span1:ident) => $arm1:expr, - InlineParent($span2:ident) => $arm2:expr, - InlinePair($span3:ident) => $arm3:expr, - Interned($span4:ident) => $arm4:expr, - ) => {{ - let span64 = $span.0; - if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE { - let $span1 = InlineCtxt::from_span($span); - $arm1 - } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE { - let $span2 = InlineParent::from_span($span); - $arm2 - } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE { - let $span3 = InlinePair::from_span($span); - $arm3 - } else { - let $span4 = Interned::from_span($span); - $arm4 - } - }}; +/// Values of this type should never actually be materialized, because they are just temporaries +/// and `Span::kind` is inlined. +enum SpanKind { + InlineCtxt(InlineCtxt), + InlineParent(InlineParent), + InlinePair(InlinePair), + Interned(Interned), } /// The dummy span has zero position, length, and context, and no parent. @@ -446,6 +427,20 @@ impl Span { .or_else(|| InlinePair::try_new_span(lo, len, ctxt, parent)) } + #[inline] + fn kind(self) -> SpanKind { + let span64 = self.0; + if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE { + SpanKind::InlineCtxt(InlineCtxt::from_span(self)) + } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE { + SpanKind::InlineParent(InlineParent::from_span(self)) + } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE { + SpanKind::InlinePair(InlinePair::from_span(self)) + } else { + SpanKind::Interned(Interned::from_span(self)) + } + } + #[inline] pub fn data(self) -> SpanData { let data = self.data_untracked(); @@ -459,12 +454,11 @@ impl Span { /// This function must not be used outside the incremental engine. #[inline] pub fn data_untracked(self) -> SpanData { - match_span_kind! { - self, - InlineCtxt(span) => span.data(), - InlineParent(span) => span.data(), - InlinePair(span) => span.data(), - Interned(span) => span.data(), + match self.kind() { + SpanKind::InlineCtxt(span) => span.data(), + SpanKind::InlineParent(span) => span.data(), + SpanKind::InlinePair(span) => span.data(), + SpanKind::Interned(span) => span.data(), } } @@ -478,20 +472,18 @@ impl Span { /// means it's not the same as an equality comparison with `DUMMY_SP`. #[inline] pub fn is_dummy(self) -> bool { - match_span_kind! { - self, - InlineCtxt(span) => span.lo == 0 && span.len == 0, - InlineParent(span) => span.lo == 0 && span.len == 0, - InlinePair(span) => span.lo == 0 && span.len == 0, - Interned(span) => span.data().is_dummy(), + match self.kind() { + SpanKind::InlineCtxt(span) => span.lo == 0 && span.len == 0, + SpanKind::InlineParent(span) => span.lo == 0 && span.len == 0, + SpanKind::InlinePair(span) => span.lo == 0 && span.len == 0, + SpanKind::Interned(span) => span.data().is_dummy(), } } #[inline] pub fn map_ctxt(self, map: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span { - let data = match_span_kind! { - self, - InlineCtxt(span) => { + let data = match self.kind() { + SpanKind::InlineCtxt(span) => { // This format occurs 1-2 orders of magnitude more often than others (#125017), // so it makes sense to micro-optimize it to avoid `span.data()` and `Span::new()`. let new_ctxt = map(SyntaxContext::from_u32(span.ctxt)); @@ -501,10 +493,10 @@ impl Span { } else { span.data().with_ctxt(new_ctxt) }; - }, - InlineParent(span) => span.data(), - InlinePair(span) => span.data(), - Interned(span) => span.data(), + } + SpanKind::InlineParent(span) => span.data(), + SpanKind::InlinePair(span) => span.data(), + SpanKind::Interned(span) => span.data(), }; data.with_ctxt(map(data.ctxt)) @@ -514,37 +506,35 @@ impl Span { /// It's a cut-down version of `data_untracked` and doesn't require taking the interner lock. #[inline] pub fn ctxt(self) -> SyntaxContext { - let ctxt32 = match_span_kind! { - self, - InlineCtxt(span) => span.ctxt, - InlineParent(_span) => return SyntaxContext::root(), - InlinePair(span) => span.ctxt, - Interned(span) => span.ctxt, - }; - SyntaxContext::from_u32(ctxt32) + SyntaxContext::from_u32(match self.kind() { + SpanKind::InlineCtxt(span) => span.ctxt, + SpanKind::InlineParent(_span) => return SyntaxContext::root(), + SpanKind::InlinePair(span) => span.ctxt, + SpanKind::Interned(span) => span.ctxt, + }) } #[inline] pub fn with_parent(self, parent: Option) -> Span { - let data = match_span_kind! { - self, - InlineCtxt(span) => { + let data = match self.kind() { + SpanKind::InlineCtxt(span) => { // This format occurs 1-2 orders of magnitude more often than others (#126544), // so it makes sense to micro-optimize it to avoid `span.data()`. match parent { None => return self, Some(parent) - if let Some(span) = - Span::try_new_span_with_parent(span.lo, span.len, span.ctxt, parent) => + if let Some(span) = Span::try_new_span_with_parent( + span.lo, span.len, span.ctxt, parent, + ) => { return span; } _ => span.data(), } - }, - InlineParent(span) => span.data(), - InlinePair(span) => span.data(), - Interned(span) => span.data(), + } + SpanKind::InlineParent(span) => span.data(), + SpanKind::InlinePair(span) => span.data(), + SpanKind::Interned(span) => span.data(), }; if let Some(old_parent) = data.parent { @@ -556,12 +546,11 @@ impl Span { #[inline] pub fn parent(self) -> Option { let to_parent = |parent| Some(LocalDefId { local_def_index: DefIndex::from_u32(parent) }); - match_span_kind! { - self, - InlineCtxt(_span) => None, - InlineParent(span) => to_parent(span.parent), - InlinePair(span) => to_parent(span.parent), - Interned(span) => span.data().parent, + match self.kind() { + SpanKind::InlineCtxt(_span) => None, + SpanKind::InlineParent(span) => to_parent(span.parent), + SpanKind::InlinePair(span) => to_parent(span.parent), + SpanKind::Interned(span) => span.data().parent, } } From 63bab495f9588c6daed6716f7bdca896d0a4df17 Mon Sep 17 00:00:00 2001 From: Vlad Mashkautsan <47895995+vladmashk@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:39:02 +0100 Subject: [PATCH 2/6] Fix typo in char.to_lowercase documentation --- library/core/src/char/methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 2a7b088070bd6..137e2e2d767b2 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1447,7 +1447,7 @@ impl char { /// /// ## Greek sigma /// - /// In Greek, the letter simga (uppercase 'Σ') has two lowercase forms: + /// In Greek, the letter sigma (uppercase 'Σ') has two lowercase forms: /// 'σ' which is used in most situations, and 'ς' which appears only /// at the end of a word. [`char::to_lowercase()`] always uses the first form: /// From 2d9d95ad43a65e7e7565106849a16d147822fa9e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 11 Nov 2026 12:08:08 +0100 Subject: [PATCH 3/6] Update to ar_archive_writer 0.5.5 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a677f5957794..53de49d87d5f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,9 +177,9 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ar_archive_writer" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" +checksum = "523e5e187fff36fcb8d1d19ccf0ca4ca43ec3d158316057f66dc6f72716bc5ec" dependencies = [ "object 0.39.1", ] From 1a97ea16c051a0667b74a2d63945b04799dfd431 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 8 Sep 2025 16:47:37 +0000 Subject: [PATCH 4/6] Remove `-Z stack-protector=basic` This is a part of the stack-protector stabilization The `-Z stack-protector=basic` mode (`-fstack-protector`) uses heuristics that are specifically designed to catch old-C-style string manipulation. This is not a good fit to Rust, which does not perform much unsafe C-style string manipulation. As far as I can tell, nobody has been asking for it, and few people are using it even in today's C - modern distros (e.g. Debian) tend to use `-fstack-protector-strong`. Therefore, `-Z stack-protector=basic` has been **removed**. If anyone is interested in it, they are welcome to add it back as an unstable option. --- bootstrap.example.toml | 3 +- compiler/rustc_codegen_gcc/src/gcc_util.rs | 1 - compiler/rustc_codegen_llvm/src/attributes.rs | 1 - compiler/rustc_session/src/options.rs | 3 +- .../src/options/mitigation_coverage.rs | 4 --- compiler/rustc_target/src/spec/mod.rs | 6 ---- .../stack-protector-heuristics-effect.rs | 17 +---------- ...otector-heuristics-effect-windows-32bit.rs | 25 ++++------------ ...otector-heuristics-effect-windows-64bit.rs | 29 +++++-------------- .../stack-protector-heuristics-effect.rs | 20 +++---------- tests/codegen-llvm/stack-protector.rs | 9 +----- .../no-stack-protector-basic.rs | 5 ++++ .../no-stack-protector-basic.stderr | 2 ++ ...n-stack-protector-unsupported.basic.stderr | 4 --- .../warn-stack-protector-unsupported.rs | 4 +-- 15 files changed, 30 insertions(+), 103 deletions(-) create mode 100644 tests/ui/stack-protector/no-stack-protector-basic.rs create mode 100644 tests/ui/stack-protector/no-stack-protector-basic.stderr delete mode 100644 tests/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 6c2e3917fb697..6740de03eb800 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -785,8 +785,7 @@ # Indicates whether stack protectors should be used # via the unstable option `-Zstack-protector`. # -# Valid options are : `none`(default),`basic`,`strong`, or `all`. -# `strong` and `basic` options may be buggy and are not recommended, see rust-lang/rust#114903. +# Valid options are : `none`(default), `strong`, or `all`. #rust.stack-protector = "none" # Prints each test name as it is executed, to help debug issues in the test harness itself. diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index b0442cdfb11cc..ab6f78866c94a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -307,7 +307,6 @@ pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> { match sess.stack_protector() { StackProtector::All => context.add_command_line_option("-fstack-protector-all"), StackProtector::Strong => context.add_command_line_option("-fstack-protector-strong"), - StackProtector::Basic => context.add_command_line_option("-fstack-protector"), StackProtector::None => (), } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index d74c6dd68564b..b8b2af4221e63 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -401,7 +401,6 @@ fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll A StackProtector::None => return None, StackProtector::All => AttributeKind::StackProtectReq, StackProtector::Strong => AttributeKind::StackProtectStrong, - StackProtector::Basic => AttributeKind::StackProtect, }; Some(sspattr.create_attr(cx.llcx)) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ef503a627a40f..d8da338f83d87 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -945,8 +945,7 @@ mod desc { "either no value or one of `legacy` (the default), `off`, or `next`"; pub(crate) const parse_annotate_moves: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc.), or a size limit in bytes"; - pub(crate) const parse_stack_protector: &str = - "one of (`none` (default), `basic`, `strong`, or `all`)"; + pub(crate) const parse_stack_protector: &str = "one of (`none` (default), `strong`, or `all`)"; pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)"; pub(crate) const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; diff --git a/compiler/rustc_session/src/options/mitigation_coverage.rs b/compiler/rustc_session/src/options/mitigation_coverage.rs index 22a6e4db9c57f..5dea83e5751e0 100644 --- a/compiler/rustc_session/src/options/mitigation_coverage.rs +++ b/compiler/rustc_session/src/options/mitigation_coverage.rs @@ -20,7 +20,6 @@ impl DeniedPartialMitigationLevel { pub fn level_str(&self) -> &'static str { match self { DeniedPartialMitigationLevel::StackProtector(StackProtector::All) => "=all", - DeniedPartialMitigationLevel::StackProtector(StackProtector::Basic) => "=basic", DeniedPartialMitigationLevel::StackProtector(StackProtector::Strong) => "=strong", // currently `=disabled` should not appear DeniedPartialMitigationLevel::Enabled(false) => "=disabled", @@ -36,9 +35,6 @@ impl std::fmt::Display for DeniedPartialMitigationLevel { DeniedPartialMitigationLevel::StackProtector(StackProtector::All) => { write!(f, "all") } - DeniedPartialMitigationLevel::StackProtector(StackProtector::Basic) => { - write!(f, "basic") - } DeniedPartialMitigationLevel::StackProtector(StackProtector::Strong) => { write!(f, "strong") } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b3839b23d9a10..476970b10ea3a 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1204,12 +1204,6 @@ crate::target_spec_enum! { /// Disable stack canary generation. None = "none", - /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see - /// llvm/docs/LangRef.rst). This triggers stack canary generation in - /// functions which contain an array of a byte-sized type with more than - /// eight elements. - Basic = "basic", - /// On LLVM, mark all generated LLVM functions with the `sspstrong` /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary /// generation in functions which either contain an array, or which take diff --git a/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs index 49fcfce438c2e..9366e949856d4 100644 --- a/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs @@ -1,4 +1,4 @@ -//@ revisions: all strong basic none missing +//@ revisions: all strong none missing //@ assembly-output: emit-asm //@ ignore-apple slightly different policy on stack protection of arrays //@ ignore-msvc stack check code uses different function names @@ -6,7 +6,6 @@ //@ ignore-wasm32-unknown-unknown //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong -//@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -Z allow-partial-mitigations=stack-protector @@ -19,7 +18,6 @@ pub fn emptyfn() { // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -37,7 +35,6 @@ pub fn array_char(f: fn(*const char)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -53,7 +50,6 @@ pub fn array_u8_1(f: fn(*const u8)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -70,7 +66,6 @@ pub fn array_u8_small(f: fn(*const u8)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -86,7 +81,6 @@ pub fn array_u8_large(f: fn(*const u8)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -105,7 +99,6 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -132,7 +125,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -148,7 +140,6 @@ pub fn local_string_addr_taken(f: fn(&String)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -177,7 +168,6 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -214,7 +204,6 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -251,7 +240,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -275,7 +263,6 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -287,7 +274,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -299,7 +285,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs index c0b81f9ba2fdf..af808ad9d9257 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs @@ -1,11 +1,10 @@ -//@ revisions: all strong basic none missing +//@ revisions: all strong none missing //@ assembly-output: emit-asm //@ only-windows //@ only-msvc //@ ignore-64bit 64-bit table based SEH has slightly different behaviors than classic SEH //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong -//@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -Z allow-partial-mitigations=stack-protector -C opt-level=2 -Z merge-functions=disabled -Cpanic=abort -Cdebuginfo=1 @@ -18,7 +17,6 @@ pub fn emptyfn() { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie } @@ -36,7 +34,6 @@ pub fn array_char(f: fn(*const char)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -54,7 +51,6 @@ pub fn array_u8_1(f: fn(*const u8)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -70,10 +66,10 @@ pub fn array_u8_small(f: fn(*const u8)) { f(&b as *const _); // Small arrays do not lead to stack protection by the 'basic' heuristic. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -88,10 +84,10 @@ pub fn array_u8_large(f: fn(*const u8)) { // Since `a` is a byte array with size greater than 8, the basic heuristic // will also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -109,10 +105,10 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // Since `a` is a byte array in the LLVM output, the basic heuristic will // also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -138,10 +134,10 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // } // EOF // ``` + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -159,7 +155,6 @@ pub fn local_string_addr_taken(f: fn(&String)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -190,7 +185,6 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -229,7 +223,6 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -260,7 +253,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -302,7 +294,6 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -316,7 +307,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -330,7 +320,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -352,12 +341,10 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // function and into `f()`, this is implemented with pass-by-reference // using a suitably constructed fat-pointer (as if the functions // accepted &[u8]). This function therefore doesn't need dynamic array - // alloca, and is therefore not protected by the `strong` or `basic` - // heuristics. + // alloca, and is therefore not protected by the `strong` heuristic. // all-NOT: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs index 87d3ba54d10af..c4e9e760edfe5 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs @@ -1,11 +1,10 @@ -//@ revisions: all strong basic none missing +//@ revisions: all strong none missing //@ assembly-output: emit-asm //@ only-windows //@ only-msvc //@ ignore-32bit 64-bit table based SEH has slightly different behaviors than classic SEH //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong -//@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -Z allow-partial-mitigations=stack-protector -C opt-level=2 -Z merge-functions=disabled -Cpanic=abort @@ -17,7 +16,6 @@ pub fn emptyfn() { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie } @@ -36,7 +34,6 @@ pub fn array_char(f: fn(*const char)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -55,7 +52,6 @@ pub fn array_u8_1(f: fn(*const u8)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -72,10 +68,10 @@ pub fn array_u8_small(f: fn(*const u8)) { f(&b as *const _); // Small arrays do not lead to stack protection by the 'basic' heuristic. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -91,10 +87,10 @@ pub fn array_u8_large(f: fn(*const u8)) { // Since `a` is a byte array with size greater than 8, the basic heuristic // will also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -113,10 +109,10 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // Since `a` is a byte array in the LLVM output, the basic heuristic will // also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -133,7 +129,7 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // This function takes the address of a local variable taken. Although this // address is never used as a way to refer to stack memory, the `strong` - // heuristic adds stack smash protection. This is also the case in C++: + // heuristic adds stack smash protection. This is also the case C++: // ``` // cat << EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk // #include @@ -146,7 +142,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -165,7 +160,6 @@ pub fn local_string_addr_taken(f: fn(&String)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -192,12 +186,11 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // Even though the local variable conceptually has its address taken, as // it's passed by reference to the trait function, the use of the reference - // is easily inlined. There is therefore no stack smash protection even with + // is easily inlined. There is therefore no stack smash protectionen with // the `strong` heuristic. // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -237,7 +230,6 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -269,7 +261,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // all: __security_check_cookie // strong: __security_check_cookie - // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -312,7 +303,6 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -327,7 +317,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -342,7 +331,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // all: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -365,12 +353,11 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // function and into `f()`, this is implemented with pass-by-reference // using a suitably constructed fat-pointer (as if the functions // accepted &[u8]). This function therefore doesn't need dynamic array - // alloca, and is therefore not protected by the `strong` or `basic` - // heuristics. + // alloca, and is therefore not protected by the `strong` heuristic. // all-NOT: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie + // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs index 7b802e52ad7c9..aa6bbf7168b47 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs @@ -1,4 +1,4 @@ -//@ revisions: all strong basic none missing +//@ revisions: all strong none missing //@ assembly-output: emit-asm //@ ignore-apple slightly different policy on stack protection of arrays //@ ignore-msvc stack check code uses different function names @@ -6,7 +6,6 @@ //@ ignore-wasm32-unknown-unknown //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong -//@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -Z allow-partial-mitigations=stack-protector @@ -24,7 +23,6 @@ pub fn emptyfn() { // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -42,7 +40,6 @@ pub fn array_char(f: fn(*const char)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -58,7 +55,6 @@ pub fn array_u8_1(f: fn(*const u8)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -72,10 +68,10 @@ pub fn array_u8_small(f: fn(*const u8)) { f(&b as *const _); // Small arrays do not lead to stack protection by the 'basic' heuristic. + // (basic is not currently supported, leaving the test anyway). // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -88,10 +84,10 @@ pub fn array_u8_large(f: fn(*const u8)) { // Since `a` is a byte array with size greater than 8, the basic heuristic // will also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -107,10 +103,10 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // Since `a` is a byte array in the LLVM output, the basic heuristic will // also protect this function. + // (basic is not currently supported, leaving the test anyway). // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -137,7 +133,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -153,7 +148,6 @@ pub fn local_string_addr_taken(f: fn(&String)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -182,7 +176,6 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -219,7 +212,6 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -248,7 +240,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // all: __stack_chk_fail // strong: __stack_chk_fail - // basic: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -288,7 +279,6 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -300,7 +290,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } @@ -312,7 +301,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // all: __stack_chk_fail // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } diff --git a/tests/codegen-llvm/stack-protector.rs b/tests/codegen-llvm/stack-protector.rs index 1d8657b0b6226..5cd6cf4b0f8b5 100644 --- a/tests/codegen-llvm/stack-protector.rs +++ b/tests/codegen-llvm/stack-protector.rs @@ -1,8 +1,7 @@ -//@ revisions: all strong basic none +//@ revisions: all strong none //@ ignore-nvptx64 stack protector not supported //@ [all] compile-flags: -Z stack-protector=all -Z allow-partial-mitigations=stack-protector //@ [strong] compile-flags: -Z stack-protector=strong -Z allow-partial-mitigations=stack-protector -//@ [basic] compile-flags: -Z stack-protector=basic -Z allow-partial-mitigations=stack-protector #![crate_type = "lib"] @@ -22,12 +21,6 @@ pub fn foo() { // strong-NOT: attributes #0 = { {{.*}}sspreq {{.*}} } // strong-NOT: attributes #0 = { {{.*}}ssp {{.*}} } - // basic-NOT: attributes #0 = { {{.*}}sspreq {{.*}} } - // basic-NOT: attributes #0 = { {{.*}}sspstrong {{.*}} } - // basic: attributes #0 = { {{.*}}ssp {{.*}} } - // basic-NOT: attributes #0 = { {{.*}}sspreq {{.*}} } - // basic-NOT: attributes #0 = { {{.*}}sspstrong {{.*}} } - // none-NOT: attributes #0 = { {{.*}}sspreq {{.*}} } // none-NOT: attributes #0 = { {{.*}}sspstrong {{.*}} } // none-NOT: attributes #0 = { {{.*}}ssp {{.*}} } diff --git a/tests/ui/stack-protector/no-stack-protector-basic.rs b/tests/ui/stack-protector/no-stack-protector-basic.rs new file mode 100644 index 0000000000000..2e42da5695373 --- /dev/null +++ b/tests/ui/stack-protector/no-stack-protector-basic.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -Z stack-protector=basic + +pub fn main(){} + +//~? ERROR incorrect value `basic` for unstable option `stack-protector` diff --git a/tests/ui/stack-protector/no-stack-protector-basic.stderr b/tests/ui/stack-protector/no-stack-protector-basic.stderr new file mode 100644 index 0000000000000..7ee0f6ae03f3d --- /dev/null +++ b/tests/ui/stack-protector/no-stack-protector-basic.stderr @@ -0,0 +1,2 @@ +error: incorrect value `basic` for unstable option `stack-protector` - one of (`none` (default), `strong`, or `all`) was expected + diff --git a/tests/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr b/tests/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr deleted file mode 100644 index f7a1ee39fb9af..0000000000000 --- a/tests/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr +++ /dev/null @@ -1,4 +0,0 @@ -warning: `-Z stack-protector=basic` is not supported for target nvptx64-nvidia-cuda and will be ignored - -warning: 1 warning emitted - diff --git a/tests/ui/stack-protector/warn-stack-protector-unsupported.rs b/tests/ui/stack-protector/warn-stack-protector-unsupported.rs index a635976c8427b..c9505fe8b93db 100644 --- a/tests/ui/stack-protector/warn-stack-protector-unsupported.rs +++ b/tests/ui/stack-protector/warn-stack-protector-unsupported.rs @@ -1,10 +1,9 @@ //@ build-pass -//@ revisions: all strong basic +//@ revisions: all strong //@ compile-flags: --target nvptx64-nvidia-cuda //@ needs-llvm-components: nvptx //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong -//@ [basic] compile-flags: -Z stack-protector=basic #![crate_type = "lib"] #![feature(no_core, lang_items)] @@ -27,4 +26,3 @@ pub fn main(){} //[all]~? WARN `-Z stack-protector=all` is not supported for target nvptx64-nvidia-cuda and will be ignored //[strong]~? WARN `-Z stack-protector=strong` is not supported for target nvptx64-nvidia-cuda and will be ignored -//[basic]~? WARN `-Z stack-protector=basic` is not supported for target nvptx64-nvidia-cuda and will be ignored From a8afb0f72377338457ea766ac8a8430886b618c8 Mon Sep 17 00:00:00 2001 From: Joseph Zhao Date: Wed, 11 Nov 2026 13:57:15 +0000 Subject: [PATCH 5/6] std: don't overflow when rounding up huge thread stack sizes on unix * std: don't overflow when rounding up huge thread stack sizes on unix * Update library/std/src/thread/tests.rs Co-authored-by: Joseph Zhao <65984904+programskillforverification@users.noreply.github.com> Co-authored-by: Ralf Jung --- library/std/src/sys/thread/unix.rs | 7 ++++--- library/std/src/thread/tests.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index b7908dea2b5de..400ee36466620 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -86,13 +86,14 @@ impl Thread { // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. let page_size = sys::pal::conf::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + let stack_size = stack_size.checked_next_multiple_of(page_size); // Some libc implementations, e.g. musl, place an upper bound // on the stack size, in which case we can only gracefully return // an error here. - if libc::pthread_attr_setstacksize(attr.get(), stack_size) != 0 { + if stack_size.is_none_or(|stack_size| { + libc::pthread_attr_setstacksize(attr.get(), stack_size) != 0 + }) { return Err(io::const_error!( io::ErrorKind::InvalidInput, "invalid stack size" diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 252524a4cfed7..4b14e782193c2 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -447,3 +447,14 @@ fn test_minimal_thread_stack() { assert_eq!(before, 0); assert_eq!(COUNT.load(Ordering::Relaxed), 1); } + +// Test that a stack size too large to round up to the page size is an error. +// The cfg mirrors the one around the rounding in the unix `Thread::new`. +#[cfg(all(unix, not(any(target_os = "espidf", target_os = "nuttx"))))] +// FIXME(https://github.com/rust-lang/miri/issues/5594): Miri ignores the requested stack size. +#[cfg_attr(skip_unsupported_miri_tests, ignore)] +#[test] +fn test_huge_thread_stack() { + let builder = thread::Builder::new().stack_size(usize::MAX); + assert!(builder.spawn(|| {}).is_err()); +} From 381ba5565bcbc7759dd38a3be52ac3aab34045df Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 11 Nov 2026 22:34:02 +0800 Subject: [PATCH 6/6] test: fix `unsized.rs` debuginfo test for cdb 10.0.28000.2957+ This was a cdb test that was failing locally when I updated Windows SDK to 10.0.28000.2957 (and thus cdb and WinDbg). Seems that for whatever reason eventual visualizations for `c` and `_box` changed between these cdb (WinDbg) versions. --- tests/debuginfo/unsized.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/debuginfo/unsized.rs b/tests/debuginfo/unsized.rs index ec5ab884c5b7b..db531701a9d4b 100644 --- a/tests/debuginfo/unsized.rs +++ b/tests/debuginfo/unsized.rs @@ -22,6 +22,9 @@ // === CDB TESTS =================================================================================== +// NB: `c` and `_box` format differences between 10.0.26100.0 and 10.0.28000.2957. +//@ min-cdb-version: 10.0.28000.2957 + //@ cdb-command: g //@ cdb-command:dx a //@ cdb-check:a [Type: ref$ > >] @@ -34,14 +37,10 @@ //@ cdb-check: [...] length : 0x4 [Type: unsigned [...]int[...] //@ cdb-command:dx c -//@ cdb-check:c [Type: ref$ > >] -//@ cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] -//@ cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] +//@ cdb-check:c : 7 [Type: i32 (derived from ref$ > >)] //@ cdb-command:dx _box -//@ cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] -//@ cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] -//@ cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] +//@ cdb-check:_box : 8 [Type: i32 (derived from alloc::boxed::Box >,alloc::alloc::Global>)] struct Foo { value: T,