From 31ce9e781d1cdd57fbb6c7a0b86964de9e4012d0 Mon Sep 17 00:00:00 2001 From: shua Date: Thu, 26 Feb 2026 23:39:46 +0100 Subject: [PATCH 01/71] debuginfo: slices are DW_TAG_array_type's --- .../src/debuginfo/metadata.rs | 49 ++++++++++++------- src/etc/gdb_lookup.py | 1 + src/etc/gdb_providers.py | 20 ++++++++ src/etc/rust_types.py | 3 ++ tests/debuginfo/strings-and-strs.rs | 14 ++++++ 5 files changed, 68 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0fd47cb15f286..26d98ec13cc2b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -152,7 +152,20 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type)) ); - let pointee_type_di_node = type_di_node(cx, pointee_type); + let pointee_type_di_node = match pointee_type.kind() { + // `&[T]` will look like `{ data_ptr: *const T, length: usize }` + ty::Slice(element_type) => type_di_node(cx, *element_type), + // `&str` will look like `{ data_ptr: *const u8, length: usize }` + ty::Str => type_di_node(cx, cx.tcx.types.u8), + + // `&dyn K` will look like `{ pointer: _, vtable: _}` + // any Adt `Foo` containing an unsized type (eg `&[_]` or `&dyn _`) + // will look like `{ data_ptr: *const Foo, length: usize }` + // and thin pointers `&Foo` will just look like `*const Foo`. + // + // in all those cases, we just use the pointee_type + _ => type_di_node(cx, pointee_type), + }; return_if_di_node_created_in_meantime!(cx, unique_type_id); @@ -389,26 +402,11 @@ fn build_dyn_type_di_node<'ll, 'tcx>( } /// Create debuginfo for `[T]` and `str`. These are unsized. -/// -/// NOTE: We currently emit just emit the debuginfo for the element type here -/// (i.e. `T` for slices and `u8` for `str`), so that we end up with -/// `*const T` for the `data_ptr` field of the corresponding wide-pointer -/// debuginfo of `&[T]`. -/// -/// It would be preferable and more accurate if we emitted a DIArray of T -/// without an upper bound instead. That is, LLVM already supports emitting -/// debuginfo of arrays of unknown size. But GDB currently seems to end up -/// in an infinite loop when confronted with such a type. -/// -/// As a side effect of the current encoding every instance of a type like -/// `struct Foo { unsized_field: [u8] }` will look like -/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the -/// slice is zero, then accessing `unsized_field` in the debugger would -/// result in an out-of-bounds access. fn build_slice_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, slice_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let element_type = match slice_type.kind() { ty::Slice(element_type) => *element_type, @@ -423,7 +421,20 @@ fn build_slice_type_di_node<'ll, 'tcx>( let element_type_di_node = type_di_node(cx, element_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); - DINodeCreationResult { di_node: element_type_di_node, already_stored_in_typemap: false } + let (size, align) = cx.spanned_size_and_align_of(slice_type, span); + let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, -1) }; + let subscripts = &[subrange]; + let di_node = unsafe { + llvm::LLVMDIBuilderCreateArrayType( + DIB(cx), + size.bits(), + align.bits() as u32, + element_type_di_node, + subscripts.as_ptr(), + subscripts.len() as c_uint, + ) + }; + DINodeCreationResult { di_node, already_stored_in_typemap: false } } /// Get the debuginfo node for the given type. @@ -454,7 +465,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( } ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t), ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t, span), - ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), + ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id, span), ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py index c70944790d2b5..ae9696fa2ca92 100644 --- a/src/etc/gdb_lookup.py +++ b/src/etc/gdb_lookup.py @@ -103,6 +103,7 @@ def __call__(self, valobj): printer.add(RustType.StdString, StdStringProvider) printer.add(RustType.StdOsString, StdOsStringProvider) printer.add(RustType.StdStr, StdStrProvider) +printer.add(RustType.StdBoxStr, StdBoxStrProvider) printer.add(RustType.StdSlice, StdSliceProvider) printer.add(RustType.StdVec, StdVecProvider) printer.add(RustType.StdVecDeque, StdVecDequeProvider) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index bd27998b37706..7d50de0d3050d 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -142,6 +142,20 @@ def display_hint(): return "array" +class StdBoxStrProvider(printer_base): + def __init__(self, valobj): + self._valobj = valobj + self._length = int(valobj["length"]) + self._data_ptr = valobj["data_ptr"] + + def to_string(self): + return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) + + @staticmethod + def display_hint(): + return "string" + + class StdVecProvider(printer_base): def __init__(self, valobj): self._valobj = valobj @@ -203,6 +217,12 @@ def __init__(self, valobj, is_atomic=False): self._is_atomic = is_atomic self._ptr = unwrap_unique_or_non_null(valobj["ptr"]) self._value = self._ptr["data" if is_atomic else "value"] + # FIXME(shua): the debuginfo template type should be 'str' not 'u8' + if self._ptr.type.target().name == "alloc::rc::RcInner": + length = self._valobj["ptr"]["pointer"]["length"] + u8_ptr_ty = gdb.Type.pointer(gdb.lookup_type("u8")) + ptr = self._value.address.reinterpret_cast(u8_ptr_ty) + self._value = ptr.lazy_string(encoding="utf-8", length=length) self._strong = unwrap_scalar_wrappers(self._ptr["strong"]) self._weak = unwrap_scalar_wrappers(self._ptr["weak"]) - 1 diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py index ca462654e44e6..1cc526a25604a 100644 --- a/src/etc/rust_types.py +++ b/src/etc/rust_types.py @@ -37,12 +37,14 @@ class RustType(Enum): StdNonZeroNumber = 29 StdPath = 30 StdPathBuf = 31 + StdBoxStr = 32 STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") STD_STR_REGEX = re.compile(r"^&(mut )?str$") STD_SLICE_REGEX = re.compile(r"^&(mut )?\[.+\]$") STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::([a-z_]+::)+)OsString$") +STD_BOX_STR_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Box$") STD_VEC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Vec<.+>$") STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)VecDeque<.+>$") STD_BTREE_SET_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeSet<.+>$") @@ -67,6 +69,7 @@ class RustType(Enum): RustType.StdString: STD_STRING_REGEX, RustType.StdOsString: STD_OS_STRING_REGEX, RustType.StdStr: STD_STR_REGEX, + RustType.StdBoxStr: STD_BOX_STR_REGEX, RustType.StdSlice: STD_SLICE_REGEX, RustType.StdVec: STD_VEC_REGEX, RustType.StdVecDeque: STD_VEC_DEQUE_REGEX, diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 165cfcd968a67..178b92df3630a 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -23,6 +23,12 @@ //@ gdb-command:print str_in_rc //@ gdb-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x[...]}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} +//@ gdb-command:print box_str +//@ gdb-check:$6 = alloc::boxed::Box [87, 111, 114, 108, 100] + +//@ gdb-command:print rc_str +//@ gdb-check:$7 = alloc::rc::Rc {ptr: core::ptr::non_null::NonNull> {pointer: alloc::rc::RcInner {strong: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, weak: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, value: 0x[...]}}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} + // === LLDB TESTS ================================================================================== //@ lldb-command:run //@ lldb-command:v plain_string @@ -40,6 +46,12 @@ //@ lldb-command:v str_in_rc //@ lldb-check:(alloc::rc::Rc<&str, alloc::alloc::Global>) str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } +//@ lldb-command:v box_str +//@ lldb-check:(alloc::boxed::Box) box_str = { __0 = { pointer = { pointer = { data_ptr = 0x[...] "World" length = 5 } } _marker = } __1 = } + +//@ lldb-command:v rc_str +//@ lldb-check:(alloc::rc::Rc) rc_str = strong=1, weak=0 { value = "World" } + #![allow(unused_variables)] pub struct Foo<'a> { @@ -53,6 +65,8 @@ fn main() { let str_in_tuple = ("Hello", "World"); let str_in_rc = std::rc::Rc::new("Hello"); + let box_str: Box = "World".into(); + let rc_str: std::rc::Rc = "World".into(); zzz(); // #break } From 580e752ed484f7f72b3815522ecb9247195f2e0b Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 28 May 2026 11:54:13 -0400 Subject: [PATCH 02/71] add regression test --- tests/ui/parser/recover/raw-no-const-mut.rs | 9 ++++ .../ui/parser/recover/raw-no-const-mut.stderr | 46 +++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/tests/ui/parser/recover/raw-no-const-mut.rs b/tests/ui/parser/recover/raw-no-const-mut.rs index d0ae69cc30848..d350301ff5e49 100644 --- a/tests/ui/parser/recover/raw-no-const-mut.rs +++ b/tests/ui/parser/recover/raw-no-const-mut.rs @@ -28,4 +28,13 @@ fn e() { //~^ ERROR expected one of } +fn g() { + fn takes_raw_ptr(_: *const u32) {} + + let x = 0u32; + // Regression test for https://github.com/rust-lang/rust/issues/157015. + takes_raw_ptr(&raw x); + //~^ ERROR expected one of +} + fn main() {} diff --git a/tests/ui/parser/recover/raw-no-const-mut.stderr b/tests/ui/parser/recover/raw-no-const-mut.stderr index 3007134f7f5c0..91beb3b0bbb48 100644 --- a/tests/ui/parser/recover/raw-no-const-mut.stderr +++ b/tests/ui/parser/recover/raw-no-const-mut.stderr @@ -76,6 +76,23 @@ LL | x = &raw const 1; LL | x = &raw mut 1; | +++ +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` + --> $DIR/raw-no-const-mut.rs:36:24 + | +LL | takes_raw_ptr(&raw x); + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | takes_raw_ptr(&raw const x); + | +++++ +LL | takes_raw_ptr(&raw mut x); + | +++ +help: missing `,` + | +LL | takes_raw_ptr(&raw, x); + | + + error[E0425]: cannot find value `raw` in this scope --> $DIR/raw-no-const-mut.rs:7:21 | @@ -88,6 +105,12 @@ error[E0425]: cannot find value `raw` in this scope LL | f(&raw 2); | ^^^ not found in this scope +error[E0425]: cannot find value `raw` in this scope + --> $DIR/raw-no-const-mut.rs:36:20 + | +LL | takes_raw_ptr(&raw x); + | ^^^ not found in this scope + error[E0745]: cannot take address of a temporary --> $DIR/raw-no-const-mut.rs:7:17 | @@ -109,7 +132,24 @@ LL - f(&raw 2); LL + a(&raw 2); | -error: aborting due to 9 previous errors +error[E0061]: this function takes 1 argument but 2 arguments were supplied + --> $DIR/raw-no-const-mut.rs:36:5 + | +LL | takes_raw_ptr(&raw x); + | ^^^^^^^^^^^^^ - unexpected argument #2 of type `u32` + | +note: function defined here + --> $DIR/raw-no-const-mut.rs:32:8 + | +LL | fn takes_raw_ptr(_: *const u32) {} + | ^^^^^^^^^^^^^ +help: remove the extra argument + | +LL - takes_raw_ptr(&raw x); +LL + takes_raw_ptr(&raw); + | + +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0425, E0745. -For more information about an error, try `rustc --explain E0425`. +Some errors have detailed explanations: E0061, E0425, E0745. +For more information about an error, try `rustc --explain E0061`. From b459cfcf439c084ed12233e49001a990293f6e8b Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 28 May 2026 15:26:08 -0400 Subject: [PATCH 03/71] don't recover `&raw EXPR` as a missing comma --- compiler/rustc_parse/src/parser/expr.rs | 24 +++++- compiler/rustc_parse/src/parser/mod.rs | 11 +++ .../recover/raw-no-const-mut-arg-list.rs | 9 +++ .../recover/raw-no-const-mut-arg-list.stderr | 15 ++++ tests/ui/parser/recover/raw-no-const-mut.rs | 12 --- .../ui/parser/recover/raw-no-const-mut.stderr | 81 ++----------------- 6 files changed, 62 insertions(+), 90 deletions(-) create mode 100644 tests/ui/parser/recover/raw-no-const-mut-arg-list.rs create mode 100644 tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 957ed9ab55e0a..43040b9cef6aa 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -29,7 +29,7 @@ use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; -use super::diagnostics::SnapshotParser; +use super::diagnostics::{ConsumeClosingDelim, SnapshotParser}; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ @@ -1279,9 +1279,25 @@ impl<'a> Parser<'a> { }; let open_paren = self.token.span; - let seq = self - .parse_expr_paren_seq() - .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); + let seq = match self.parse_expr_paren_seq() { + Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))), + Err(err) + if self.prev_token.is_keyword(kw::Raw) + && self.expected_token_types.contains(TokenType::KwMut) + && self.expected_token_types.contains(TokenType::KwConst) + && self.token.can_begin_expr() => + { + let err_span = self.prev_token.span.to(self.token.span); + let guar = err.emit(); + // Preserve the call expression so later passes can still diagnose the callee, + // while treating the malformed `&raw ` argument as an error expression. + self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::Yes); + let err_arg = self.mk_expr_err(err_span, guar); + return self + .mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, thin_vec![err_arg])); + } + Err(err) => Err(err), + }; match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) { Ok(expr) => expr, Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err), diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index a16ddb34b3c2e..ecdf242e18321 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -917,6 +917,17 @@ impl<'a> Parser<'a> { } // Attempt to keep parsing if it was an omitted separator. + // `&raw ` already has a specific suggestion for missing + // `const`/`mut`, so don't recover `` as the next element in + // a comma-separated list. + if exp.token_type == TokenType::Comma + && self.prev_token.is_keyword(kw::Raw) + && self.expected_token_types.contains(TokenType::KwMut) + && self.expected_token_types.contains(TokenType::KwConst) + && self.token.can_begin_expr() + { + return Err(expect_err); + } self.last_unexpected_token_span = None; match f(self) { Ok(t) => { diff --git a/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs b/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs new file mode 100644 index 0000000000000..db47140b85e42 --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs @@ -0,0 +1,9 @@ +// Regression test for https://github.com/rust-lang/rust/issues/157015. + +fn takes_raw_ptr(_: *const u32) {} + +fn main() { + let x = 0u32; + takes_raw_ptr(&raw x); + //~^ ERROR expected one of +} diff --git a/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr b/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr new file mode 100644 index 0000000000000..e580497e027a8 --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr @@ -0,0 +1,15 @@ +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` + --> $DIR/raw-no-const-mut-arg-list.rs:7:24 + | +LL | takes_raw_ptr(&raw x); + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | takes_raw_ptr(&raw const x); + | +++++ +LL | takes_raw_ptr(&raw mut x); + | +++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/recover/raw-no-const-mut.rs b/tests/ui/parser/recover/raw-no-const-mut.rs index d350301ff5e49..195de7d76029a 100644 --- a/tests/ui/parser/recover/raw-no-const-mut.rs +++ b/tests/ui/parser/recover/raw-no-const-mut.rs @@ -6,8 +6,6 @@ fn a() { fn b() { [&raw const 1, &raw 2] //~^ ERROR expected one of - //~| ERROR cannot find value `raw` in this scope - //~| ERROR cannot take address of a temporary } fn c() { @@ -18,7 +16,6 @@ fn c() { fn d() { f(&raw 2); //~^ ERROR expected one of - //~| ERROR cannot find value `raw` in this scope //~| ERROR cannot find function `f` in this scope } @@ -28,13 +25,4 @@ fn e() { //~^ ERROR expected one of } -fn g() { - fn takes_raw_ptr(_: *const u32) {} - - let x = 0u32; - // Regression test for https://github.com/rust-lang/rust/issues/157015. - takes_raw_ptr(&raw x); - //~^ ERROR expected one of -} - fn main() {} diff --git a/tests/ui/parser/recover/raw-no-const-mut.stderr b/tests/ui/parser/recover/raw-no-const-mut.stderr index 91beb3b0bbb48..a6a47e651cd17 100644 --- a/tests/ui/parser/recover/raw-no-const-mut.stderr +++ b/tests/ui/parser/recover/raw-no-const-mut.stderr @@ -23,19 +23,15 @@ LL | [&raw const 1, &raw const 2] | +++++ LL | [&raw const 1, &raw mut 2] | +++ -help: missing `,` - | -LL | [&raw const 1, &raw, 2] - | + error: expected `{`, found `z` - --> $DIR/raw-no-const-mut.rs:14:18 + --> $DIR/raw-no-const-mut.rs:12:18 | LL | if x == &raw z {} | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/raw-no-const-mut.rs:14:8 + --> $DIR/raw-no-const-mut.rs:12:8 | LL | if x == &raw z {} | ^^^^^^^^^ @@ -47,7 +43,7 @@ LL | if x == &raw mut z {} | +++ error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2` - --> $DIR/raw-no-const-mut.rs:19:12 + --> $DIR/raw-no-const-mut.rs:17:12 | LL | f(&raw 2); | ^ expected one of 10 possible tokens @@ -58,13 +54,9 @@ LL | f(&raw const 2); | +++++ LL | f(&raw mut 2); | +++ -help: missing `,` - | -LL | f(&raw, 2); - | + error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1` - --> $DIR/raw-no-const-mut.rs:27:14 + --> $DIR/raw-no-const-mut.rs:24:14 | LL | x = &raw 1; | ^ expected one of 10 possible tokens @@ -76,49 +68,8 @@ LL | x = &raw const 1; LL | x = &raw mut 1; | +++ -error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` - --> $DIR/raw-no-const-mut.rs:36:24 - | -LL | takes_raw_ptr(&raw x); - | ^ expected one of 10 possible tokens - | -help: `&raw` must be followed by `const` or `mut` to be a raw reference expression - | -LL | takes_raw_ptr(&raw const x); - | +++++ -LL | takes_raw_ptr(&raw mut x); - | +++ -help: missing `,` - | -LL | takes_raw_ptr(&raw, x); - | + - -error[E0425]: cannot find value `raw` in this scope - --> $DIR/raw-no-const-mut.rs:7:21 - | -LL | [&raw const 1, &raw 2] - | ^^^ not found in this scope - -error[E0425]: cannot find value `raw` in this scope - --> $DIR/raw-no-const-mut.rs:19:8 - | -LL | f(&raw 2); - | ^^^ not found in this scope - -error[E0425]: cannot find value `raw` in this scope - --> $DIR/raw-no-const-mut.rs:36:20 - | -LL | takes_raw_ptr(&raw x); - | ^^^ not found in this scope - -error[E0745]: cannot take address of a temporary - --> $DIR/raw-no-const-mut.rs:7:17 - | -LL | [&raw const 1, &raw 2] - | ^ temporary value - error[E0425]: cannot find function `f` in this scope - --> $DIR/raw-no-const-mut.rs:19:5 + --> $DIR/raw-no-const-mut.rs:17:5 | LL | fn a() { | ------ similarly named function `a` defined here @@ -132,24 +83,6 @@ LL - f(&raw 2); LL + a(&raw 2); | -error[E0061]: this function takes 1 argument but 2 arguments were supplied - --> $DIR/raw-no-const-mut.rs:36:5 - | -LL | takes_raw_ptr(&raw x); - | ^^^^^^^^^^^^^ - unexpected argument #2 of type `u32` - | -note: function defined here - --> $DIR/raw-no-const-mut.rs:32:8 - | -LL | fn takes_raw_ptr(_: *const u32) {} - | ^^^^^^^^^^^^^ -help: remove the extra argument - | -LL - takes_raw_ptr(&raw x); -LL + takes_raw_ptr(&raw); - | - -error: aborting due to 12 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0061, E0425, E0745. -For more information about an error, try `rustc --explain E0061`. +For more information about this error, try `rustc --explain E0425`. From 3914f38be290296d63e570342d9e2eb9734ff51d Mon Sep 17 00:00:00 2001 From: shua Date: Tue, 5 May 2026 23:23:07 +0200 Subject: [PATCH 04/71] adding debuginfo tests for str repr llvm di --- tests/codegen-llvm/debuginfo-unsize-field.rs | 66 ++++++++++++++++++++ tests/debuginfo/strings-and-strs.rs | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/debuginfo-unsize-field.rs diff --git a/tests/codegen-llvm/debuginfo-unsize-field.rs b/tests/codegen-llvm/debuginfo-unsize-field.rs new file mode 100644 index 0000000000000..5d133705aa557 --- /dev/null +++ b/tests/codegen-llvm/debuginfo-unsize-field.rs @@ -0,0 +1,66 @@ +//@ compile-flags:-g -Copt-level=0 -C panic=abort + +// Check that debug information for structs with embedded str and [u8] slices is distinct from +// structs with embedded u8 + +#![crate_type = "lib"] + +// NOTE: regex for the CHECK directives, +// depending on the target, u8/usize are basic types or typedefs +// linux: !1 = !DIBasicType(name: "u8", +// win: !1 = !DIDerivedType(tag: .*, name: "u8", +// and references types are +// linux: name: "&debuginfo_unsize_field::Foo" +// win: name: "ref$" + +// CHECK: ![[U8:[0-9]+]] = !DI{{Basic|Derived}}Type({{.*}}name: "u8", + +pub struct Foo { + a: u32, + b: str, +} +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "{{&|ref\$<}}{{[^"]+}}::Foo{{>?}}", {{.*}}elements: ![[FOO_REF_ELEMS:[0-9]+]] +// CHECK: ![[FOO_REF_ELEMS]] = !{![[FOO_REF_PTR:[0-9]+]], ![[FOO_REF_LEN:[0-9]+]]} +// CHECK: ![[FOO_REF_PTR]] = !DIDerivedType(tag: DW_TAG_member, name: "data_ptr", {{.*}}baseType: ![[FOO_PTR:[0-9]+]] +// CHECK: ![[FOO_PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[FOO:[0-9]+]] +// CHECK: ![[FOO]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", {{.*}}elements: ![[FOO_ELEMS:[0-9]+]] +// CHECK: ![[FOO_ELEMS]] = !{![[FOO_A:[0-9]+]], ![[FOO_B:[0-9]+]]} +// CHECK: ![[FOO_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" +// CHECK: ![[FOO_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8_SLICE:[0-9]+]] +// +// CHECK: ![[U8_SLICE]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[U8]], {{.*}}elements: ![[U8_SLICE_ELEMS:[0-9]+]] +// CHECK: ![[U8_SLICE_ELEMS]] = !{![[U8_SLICE_RANGE:[0-9]+]]} +// this is special to embedded slices, there is no upper bound on the number of elements, +// that info is stored in the length metadata for a reference to the parent struct +// CHECK: ![[U8_SLICE_RANGE]] = !DISubrange(count: -1, lowerBound: 0) +// +// CHECK: ![[FOO_REF_LEN]] = !DIDerivedType(tag: DW_TAG_member, name: "length", {{.*}}baseType: ![[USIZE:[0-9]+]] +// CHECK: ![[USIZE]] = !DI{{Basic|Derived}}Type({{.*}}name: "usize" +pub struct Bar { + a: u32, + b: [u8], +} +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "{{&|ref\$<}}{{[^"]+}}::Bar{{>?}}", {{.*}}elements: ![[BAR_REF_ELEMS:[0-9]+]] +// CHECK: ![[BAR_REF_ELEMS]] = !{![[BAR_REF_PTR:[0-9]+]], ![[BAR_REF_LEN:[0-9]+]]} +// CHECK: ![[BAR_REF_PTR]] = !DIDerivedType(tag: DW_TAG_member, name: "data_ptr", {{.*}}baseType: ![[BAR_PTR:[0-9]+]] +// CHECK: ![[BAR_PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[BAR:[0-9]+]] +// CHECK: ![[BAR]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Bar", {{.*}}elements: ![[BAR_ELEMS:[0-9]+]] +// CHECK: ![[BAR_ELEMS]] = !{![[BAR_A:[0-9]+]], ![[BAR_B:[0-9]+]]} +// CHECK: ![[BAR_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" +// CHECK: ![[BAR_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8_SLICE]] +// CHECK: ![[BAR_REF_LEN]] = !DIDerivedType(tag: DW_TAG_member, name: "length", {{.*}}baseType: ![[USIZE:[0-9]+]] +pub struct Baz { + a: u32, + b: u8, +} +// CHECK: !DIDerivedType(tag: DW_TAG_pointer_type, name: "{{&|ref\$<}}{{[^"]+}}::Baz{{>?}}", {{.*}}baseType: ![[BAZ:[0-9]+]] +// CHECK: ![[BAZ]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Baz", {{.*}}elements: ![[BAZ_ELEMS:[0-9]+]] +// CHECK: ![[BAZ_ELEMS]] = !{![[BAZ_A:[0-9]+]], ![[BAZ_B:[0-9]+]]} +// CHECK: ![[BAZ_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" +// CHECK: ![[BAZ_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8]] + +#[no_mangle] +pub fn test<'a>(a: &'a Foo, b: &'a Bar, c: &'a Baz) -> &'a u8 { + // just use this somehow so the debuginfo isn't removed + &a.b.as_bytes()[0] +} diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 178b92df3630a..ee2702d1bcf74 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -1,4 +1,4 @@ -//@ min-gdb-version: 14.0 +//@ min-gdb-version: 15.1 // LLDB 1800+ tests were not tested in CI, broke, and now are disabled //@ ignore-lldb From 307310c2b2f871b38c666c3e6a822ad89d5cae99 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 28 May 2026 13:57:18 +0000 Subject: [PATCH 05/71] Add tests/ui/impl-trait/unpin-for-future.rs --- tests/ui/impl-trait/unpin-for-future.rs | 22 +++++++++++++++++++++ tests/ui/impl-trait/unpin-for-future.stderr | 15 ++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/ui/impl-trait/unpin-for-future.rs create mode 100644 tests/ui/impl-trait/unpin-for-future.stderr diff --git a/tests/ui/impl-trait/unpin-for-future.rs b/tests/ui/impl-trait/unpin-for-future.rs new file mode 100644 index 0000000000000..1d5918a12ad87 --- /dev/null +++ b/tests/ui/impl-trait/unpin-for-future.rs @@ -0,0 +1,22 @@ +//@ edition:2024 +// +// Tests that you can't implement Unpin for a compiler-generated future using TAIT. + +#![feature(type_alias_impl_trait)] + +use core::marker::PhantomPinned; +use core::pin::Pin; + +type MyFut = impl Future; + +async fn my_async_fn() {} + +#[define_opaque(MyFut)] +fn fut() -> MyFut { + my_async_fn() +} + +impl Unpin for MyFut {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types + +fn main() {} diff --git a/tests/ui/impl-trait/unpin-for-future.stderr b/tests/ui/impl-trait/unpin-for-future.stderr new file mode 100644 index 0000000000000..22278aa1eb333 --- /dev/null +++ b/tests/ui/impl-trait/unpin-for-future.stderr @@ -0,0 +1,15 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/unpin-for-future.rs:19:1 + | +LL | impl Unpin for MyFut {} + | ^^^^^^^^^^^^^^^----- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0117`. From b9706c0d96a0011c3f8b463cad20591b1426ec3b Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Fri, 29 May 2026 15:09:48 +0100 Subject: [PATCH 06/71] Update aarch64-unknown-freebsd target description Update the target spec & relevant documentation for aarch64-unknown-freebsd to reflect it being promoted to Tier 2. Implements MCP: https://github.com/rust-lang/compiler-team/issues/961 Signed-off-by: Kajetan Puchalski --- .../src/spec/targets/aarch64_unknown_freebsd.rs | 2 +- src/doc/rustc/src/platform-support.md | 2 +- src/doc/rustc/src/platform-support/freebsd.md | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs index 69a65e6b0f024..8ae72393b7cbf 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target: "aarch64-unknown-freebsd".into(), metadata: TargetMetadata { description: Some("ARM64 FreeBSD".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(true), std: Some(true), }, diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 26dd6b31b8991..e8f63faaed283 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -89,6 +89,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI +[`aarch64-unknown-freebsd`](platform-support/freebsd.md) | ARM64 FreeBSD [`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) @@ -261,7 +262,6 @@ target | std | host | notes -------|:---:|:----:|------- [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon -[`aarch64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | ARM64 FreeBSD [`aarch64-unknown-helenos`](platform-support/helenos.md) | ✓ | | ARM64 HelenOS [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos diff --git a/src/doc/rustc/src/platform-support/freebsd.md b/src/doc/rustc/src/platform-support/freebsd.md index 9d7218b258ec4..d1f8f009ae4cc 100644 --- a/src/doc/rustc/src/platform-support/freebsd.md +++ b/src/doc/rustc/src/platform-support/freebsd.md @@ -11,8 +11,9 @@ ## Requirements -The `x86_64-unknown-freebsd` target is Tier 2 with host tools. -`i686-unknown-freebsd` is Tier 2 without host tools. Other targets are Tier 3. +The `x86_64-unknown-freebsd` and `aarch64-unknown-freebsd` targets are Tier 2 +with host tools. `i686-unknown-freebsd` is Tier 2 without host tools. +Other targets are Tier 3. See [platform-support.md](../platform-support.md) for the full list. We commit that rustc will run on all currently supported releases of @@ -34,9 +35,10 @@ FreeBSD OS binaries use the ELF file format. ## Building Rust programs -The `x86_64-unknown-freebsd` and `i686-unknown-freebsd` artifacts are -distributed by the rust project and may be installed with rustup. Other -targets are built by the ports system and may be installed with +The `x86_64-unknown-freebsd`, `aarch64-unknown-freebsd` and +`i686-unknown-freebsd` artifacts are distributed by the rust +project and may be installed with rustup. Other targets are +built by the ports system and may be installed with [pkg(7)][pkg] or [ports(7)][ports]. By default the `i686-unknown-freebsd` target uses SSE2 instructions. To build From 649f45a2837e92fa8b3409201f5c26c6d7d10967 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 29 May 2026 10:17:45 +0200 Subject: [PATCH 07/71] ./miri test: run some tests natively --- .../pass-dep/libc/libc-epoll-blocking.rs | 1 + .../pass-dep/libc/libc-epoll-no-blocking.rs | 1 + .../miri/tests/pass-dep/libc/libc-fs-flock.rs | 1 + .../pass-dep/libc/libc-fs-permissions.rs | 1 + .../tests/pass-dep/libc/libc-fs-symlink.rs | 1 + src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 37 ++- .../pass-dep/libc/libc-socket-address.rs | 1 + .../libc/libc-socket-no-blocking-epoll.rs | 1 + .../pass-dep/libc/libc-socket-no-blocking.rs | 1 + .../miri/tests/pass-dep/libc/libc-socket.rs | 5 +- src/tools/miri/tests/pass/shims/fs.rs | 6 + src/tools/miri/tests/pass/shims/socket.rs | 5 +- src/tools/miri/tests/ui.rs | 211 +++++++++++++----- 13 files changed, 203 insertions(+), 69 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index 95f82cfba9f61..6d47687a10366 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -2,6 +2,7 @@ // test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@revisions: edge_triggered level_triggered +//@run-native use std::convert::TryInto; use std::thread; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 10fc6a5395d3b..1b8a4089dcdab 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -1,5 +1,6 @@ //@only-target: linux android illumos //@revisions: edge_triggered level_triggered +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs index d8af9224ed77a..b1103688a172a 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs @@ -2,6 +2,7 @@ //@ignore-target: solaris # Does not have flock //@ignore-target: android # Does not (always?) have flock //@compile-flags: -Zmiri-disable-isolation +//@run-native //@revisions: windows_host unix_host //@[unix_host] ignore-host: windows diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs index fd67afbe83c4b..819717fed8523 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # no libc //@ignore-host: windows # needs unix PermissionExt //@compile-flags: -Zmiri-disable-isolation +//@run-native use std::ffi::{CStr, CString}; use std::mem::MaybeUninit; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs index 52a0d978963e8..4fe66c338a045 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs @@ -2,6 +2,7 @@ //@ignore-host: windows # creating symlinks requires admin permissions on Windows //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation +//@run-native use std::ffi::CString; use std::io::ErrorKind; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 9cc8685827077..d8e5b303215b0 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation +//@run-native use std::ffi::{CStr, CString, OsString}; use std::fs::{File, canonicalize, create_dir, remove_dir, remove_file}; @@ -396,17 +397,21 @@ fn test_posix_mkstemp() { drop(file); remove_file(path).unwrap(); - let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; - for t in invalid_templates { - let ptr = CString::new(t).unwrap().into_raw(); - let fd = unsafe { libc::mkstemp(ptr) }; - let _ = unsafe { CString::from_raw(ptr) }; - // "On error, -1 is returned, and errno is set to - // indicate the error" - assert_eq!(fd, -1); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + // Test invalid inputs. We skip this on native macOS since macOS apparently does + // not bother to validate inputs. + if !cfg!(all(not(miri), target_vendor = "apple")) { + let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; + for t in invalid_templates { + let ptr = CString::new(t).unwrap().into_raw(); + let fd = unsafe { libc::mkstemp(ptr) }; + let _ = unsafe { CString::from_raw(ptr) }; + // "On error, -1 is returned, and errno is set to + // indicate the error" + assert_eq!(fd, -1, "mkstemp succeeded on invalid template {t:?}"); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } } env::set_current_dir(old_cwd).unwrap(); @@ -908,6 +913,11 @@ fn test_readv() { /// Test that vectored reads without any buffers return zero. fn test_readv_empty_bufs() { + if cfg!(all(not(miri), target_vendor = "apple")) { + // native macOS returns an error here :shrug: + return; + } + let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; @@ -1036,6 +1046,11 @@ fn test_writev() { /// Test that vectored writes without any buffers return zero. fn test_writev_empty_bufs() { + if cfg!(all(not(miri), target_vendor = "apple")) { + // native macOS returns an error here :shrug: + return; + } + let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-address.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-address.rs index 30ba4414be0c8..bb34c7de73a7f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-address.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-address.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # No libc socket on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index e615f1db39257..9833bbc2a2971 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -1,5 +1,6 @@ //@only-target: linux android illumos //@compile-flags: -Zmiri-disable-isolation +//@run-native #![feature(io_error_inprogress)] diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs index 15c3c3416e19f..de718feb47990 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs @@ -3,6 +3,7 @@ //@revisions: windows_host unix_host //@[unix_host] ignore-host: windows //@[windows_host] only-host: windows +//@run-native #![feature(io_error_inprogress)] diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index b1d0a90105ccc..edc357653d466 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # No libc socket on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; @@ -176,8 +177,8 @@ fn test_set_nosigpipe_invalid_len() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; // Value should be of type `libc::c_int` which has size 4 bytes. - // By providing a u64 of size 8 bytes we trigger an invalid length error. - let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u64).unwrap_err(); + // By providing a u16 of size 2 bytes we trigger an invalid length error. + let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u16).unwrap_err(); assert_eq!(err.kind(), ErrorKind::InvalidInput); // Check that it is the right kind of `InvalidInput`. assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 3dcb4a01bf41b..6f32e82388942 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -1,4 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation +//@run-native #![feature(io_error_more)] #![feature(io_error_uncategorized)] @@ -92,6 +93,11 @@ fn test_file() { } fn test_file_partial_reads_writes() { + if !cfg!(miri) { + // This test is not expected to work natively. + return; + } + let path1 = utils::prepare_with_content("miri_test_fs_file1.txt", b"abcdefg"); let path2 = utils::prepare_with_content("miri_test_fs_file2.txt", b"abcdefg"); diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 785be17e2c0dc..e61e63b3c6183 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # No socket support on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native use std::io::{ErrorKind, Read, Write}; use std::net::{Shutdown, TcpListener, TcpStream}; @@ -184,7 +185,7 @@ fn test_sockopt_read_timeout() { // By default, reads on blocking sockets should block indefinitely. assert_eq!(stream.read_timeout().unwrap(), None); - let short_read_timeout = Some(Duration::from_millis(10)); + let short_read_timeout = Some(Duration::from_millis(40)); stream.set_read_timeout(short_read_timeout).unwrap(); assert_eq!(stream.read_timeout().unwrap(), short_read_timeout); @@ -207,7 +208,7 @@ fn test_sockopt_write_timeout() { // By default, writes on blocking sockets should block indefinitely. assert_eq!(stream.write_timeout().unwrap(), None); - let short_write_timeout = Some(Duration::from_millis(10)); + let short_write_timeout = Some(Duration::from_millis(40)); stream.set_write_timeout(short_write_timeout).unwrap(); assert_eq!(stream.write_timeout().unwrap(), short_write_timeout); diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 1449013e0478e..633452f6052d7 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -1,13 +1,15 @@ -use std::env; +#![allow(clippy::let_and_return)] use std::num::NonZero; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; +use std::{env, fmt}; use colored::*; use regex::bytes::Regex; use ui_test::build_manager::BuildManager; use ui_test::color_eyre::eyre::{Context, Result}; +use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::dependencies::DependencyBuilder; use ui_test::per_test_config::TestConfig; @@ -17,14 +19,37 @@ use ui_test::{CommandBuilder, Config, Match, ignore_output_conflict}; #[derive(Copy, Clone, Debug)] enum Mode { - Pass, + Pass { + native: bool, + }, /// Requires annotations Fail, /// Not used for tests, but for `miri run --dep` - RunDep, + RunDep { + native: bool, + }, + /// Test must panic. Panic, } +impl Mode { + fn native(self) -> bool { + matches!(self, Mode::Pass { native: true } | Mode::RunDep { native: true }) + } +} + +impl fmt::Display for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Mode::Pass { native: false } => write!(f, "pass"), + Mode::Pass { native: true } => write!(f, "pass-native"), + Mode::Fail => write!(f, "fail"), + Mode::Panic => write!(f, "panic"), + Mode::RunDep { .. } => unreachable!(), + } + } +} + fn miri_path() -> PathBuf { env!("CARGO_BIN_EXE_miri").into() } @@ -79,7 +104,7 @@ struct WithDependencies { bless: bool, } -/// Does *not* set any args or env vars, since it is shared between the test runner and +/// Does *not* set args or (most) env vars, since it is shared between the test runner and /// run_dep_mode. fn miri_config( target: &str, @@ -90,6 +115,15 @@ fn miri_config( // Miri is rustc-like, so we create a default builder for rustc and modify it let mut program = CommandBuilder::rustc(); program.program = miri_path(); + if mode.native() { + // This means we build the program instead of running Miri. + program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); + // Use the right linker, if necessary. We use the `CC_*` variable as that is set by CI and + // unlike `CARGO_TARGET_*_LINKER` it does not require upper-casing the target. + if let Ok(linker) = env::var(format!("CC_{target}")) { + program.args.push(format!("-Clinker={linker}").into()); + } + } let mut config = Config { target: Some(target.to_owned()), @@ -103,10 +137,17 @@ fn miri_config( ..Config::rustc(path) }; + // Register custom comments. + config.custom_comments.insert("run-native", |parser, _args, span| { + // Just remember that this is present. + parser.set_custom_once("run-native", (), span); + }); + + // Adjust comment defaults. config.comment_defaults.base().exit_status = match mode { - Mode::Pass => Some(0), + Mode::Pass { .. } => Some(0), Mode::Fail => Some(1), - Mode::RunDep => None, + Mode::RunDep { .. } => None, Mode::Panic => Some(101), } .map(Spanned::dummy) @@ -123,9 +164,12 @@ fn miri_config( // keep in sync with `./miri run` config.comment_defaults.base().add_custom("edition", Edition("2021".into())); + // Building dependencies is also a "comment default". if let Some(WithDependencies { bless }) = with_dependencies { - config.comment_defaults.base().set_custom( - "dependencies", + let crate_manifest_path = Path::new("tests/deps").join("Cargo.toml"); + let dep_builder = if mode.native() { + DependencyBuilder { crate_manifest_path, ..Default::default() } + } else { DependencyBuilder { program: CommandBuilder { // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. @@ -147,12 +191,65 @@ fn miri_config( ], ..CommandBuilder::cargo() }, - crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), + crate_manifest_path, build_std: None, bless_lockfile: bless, - }, - ); + } + }; + config.comment_defaults.base().set_custom("dependencies", dep_builder); + } + + // We only want this for actual test runs, not native run-dep mode. + if matches!(mode, Mode::Pass { native: true }) { + // Overwrite "compile-flags" so that it does nothing. + // FIXME: make it just skip `-Zmiri` flags. + config.custom_comments.insert("compile-flags", |_parser, _args, _span| {}); + + // Add a default comment that interprets our custom `run-native` comment. + #[derive(Debug)] + struct NativeRunner; + config.comment_defaults.base().set_custom("native-runner", NativeRunner); + + impl Flag for NativeRunner { + fn clone_inner(&self) -> Box { + Box::new(NativeRunner) + } + fn must_be_unique(&self) -> bool { + true + } + + fn test_condition( + &self, + _config: &Config, + comments: &ui_test::Comments, + revision: &str, + ) -> bool { + let should_run = comments + .for_revision(revision) + .any(|r| r.custom.iter().any(|(k, _v)| *k == "run-native")); + // We return `true` when the test should be ignored. + let ignore = !should_run; + ignore + } + + fn post_test_action( + &self, + config: &TestConfig, + output: &std::process::Output, + build_manager: &BuildManager, + ) -> Result<(), ui_test::Errored> { + // Delegate to the native run support. + use ui_test::custom_flags::run::Run; + Run::post_test_action( + &Run { exit_code: 0, output_conflict_handling: None }, + config, + output, + build_manager, + ) + } + } } + config } @@ -177,6 +274,9 @@ fn run_tests( assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time"); config.output_conflict_handling = ignore_output_conflict; } + if mode.native() { + config.output_conflict_handling = ignore_output_conflict; + } // Add a test env var to do environment communication tests. config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); @@ -185,23 +285,26 @@ fn run_tests( // If a test ICEs, we want to see a backtrace. config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into()))); - // Add some flags we always want. - config.program.args.push( - format!( - "--sysroot={}", - env::var("MIRI_SYSROOT").expect("MIRI_SYSROOT must be set to run the ui test suite") - ) - .into(), - ); + // Add rustc/Miri flags. config.program.args.push("-Dwarnings".into()); config.program.args.push("-Dunused".into()); config.program.args.push("-Ainternal_features".into()); - if let Ok(extra_flags) = env::var("MIRIFLAGS") { - for flag in extra_flags.split_whitespace() { - config.program.args.push(flag.into()); + config.program.args.push("-Zui-testing".into()); + if !mode.native() { + config.program.args.push( + format!( + "--sysroot={}", + env::var("MIRI_SYSROOT") + .expect("MIRI_SYSROOT must be set to run the ui test suite") + ) + .into(), + ); + if let Ok(extra_flags) = env::var("MIRIFLAGS") { + for flag in extra_flags.split_whitespace() { + config.program.args.push(flag.into()); + } } } - config.program.args.push("-Zui-testing".into()); // If we're testing the native-lib functionality, then build the shared object file for testing // external C function calls and push the relevant compiler flag. @@ -288,8 +391,8 @@ regexes! { } enum Dependencies { - WithDependencies, - WithoutDependencies, + WithDeps, + WithoutDeps, } use Dependencies::*; @@ -301,12 +404,12 @@ fn ui( with_dependencies: Dependencies, tmpdir: &Path, ) -> Result<()> { - let msg = format!("## Running ui tests in {path} for {target}"); + let msg = format!("## Running {mode} ui tests in {path} for {target}"); println!("{}", msg.green().bold()); let with_dependencies = match with_dependencies { - WithDependencies => true, - WithoutDependencies => false, + WithDeps => true, + WithoutDeps => false, }; run_tests(mode, path, target, with_dependencies, tmpdir) .with_context(|| format!("ui tests in {path} for {target} failed")) @@ -331,17 +434,27 @@ fn main() -> Result<()> { // Check whether this is a `./miri run` invocation if let Ok(mode) = env::var("MIRI_RUN_MODE") { - return run_mode(target, mode); + return run_with_deps(target, mode); } - ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; - ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies, tmpdir.path())?; - ui(Mode::Panic, "tests/panic", &target, WithDependencies, tmpdir.path())?; - ui(Mode::Fail, "tests/fail", &target, WithoutDependencies, tmpdir.path())?; - ui(Mode::Fail, "tests/fail-dep", &target, WithDependencies, tmpdir.path())?; + ui(Mode::Pass { native: false }, "tests/pass", &target, WithoutDeps, tmpdir.path())?; + ui(Mode::Pass { native: false }, "tests/pass-dep", &target, WithDeps, tmpdir.path())?; + if target == host { + ui(Mode::Pass { native: true }, "tests/pass", &target, WithoutDeps, tmpdir.path())?; + ui(Mode::Pass { native: true }, "tests/pass-dep", &target, WithDeps, tmpdir.path())?; + } + ui(Mode::Panic, "tests/panic", &target, WithDeps, tmpdir.path())?; + ui(Mode::Fail, "tests/fail", &target, WithoutDeps, tmpdir.path())?; + ui(Mode::Fail, "tests/fail-dep", &target, WithDeps, tmpdir.path())?; if cfg!(all(unix, feature = "native-lib")) && target == host { - ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?; - ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?; + ui( + Mode::Pass { native: false }, + "tests/native-lib/pass", + &target, + WithoutDeps, + tmpdir.path(), + )?; + ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDeps, tmpdir.path())?; } // We only enable GenMC tests when the `genmc` feature is enabled, but also only on platforms we support: @@ -354,30 +467,19 @@ fn main() -> Result<()> { target_endian = "little" )) && host == target { - ui(Mode::Pass, "tests/genmc/pass", &target, WithDependencies, tmpdir.path())?; - ui(Mode::Fail, "tests/genmc/fail", &target, WithDependencies, tmpdir.path())?; + ui(Mode::Pass { native: false }, "tests/genmc/pass", &target, WithDeps, tmpdir.path())?; + ui(Mode::Fail, "tests/genmc/fail", &target, WithDeps, tmpdir.path())?; } Ok(()) } -fn run_mode(target: String, mode: String) -> Result<()> { +fn run_with_deps(target: String, mode: String) -> Result<()> { let native = mode == "native"; let mut config = - miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); + miri_config(&target, "", Mode::RunDep { native }, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice - if native { - // Patch things up so that we actually compile the program. - config.program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); - config.comment_defaults.base().set_custom( - "dependencies", - DependencyBuilder { - crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), - ..Default::default() - }, - ); - } config.fill_host_and_target()?; // Reset `args` (otherwise we'll get JSON output). config.program.args = vec![]; @@ -385,7 +487,8 @@ fn run_mode(target: String, mode: String) -> Result<()> { // Compute the actual Miri invocation command. let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); let mut cmd = test_config.config.program.build(&test_config.config.out_dir); - // For some reason we need to set the target ourselves. + // We are not using `test_config.build_command` (as that would require us to know the filename + // we are invoking), so we need to set the target ourselves. cmd.arg("--target").arg(&target); // Also forward arguments to the program (skipping the binary name). // We don't put this in the `config` since we don't want it to affect the dependency build. @@ -407,8 +510,8 @@ fn run_mode(target: String, mode: String) -> Result<()> { if native { // We just built the program, we still have to run it. We can't use the ui_test `Run` flag - // as that needs an actual BuildManager, not just the one-off stub we have here. So we - // implement the core logic ourselves. + // as (a) that always captures the output, and (b) that needs an actual BuildManager, not + // just the one-off stub we have here. So we implement the core logic ourselves. // First, figure out the output binary by re-running the compiler with `--print`. cmd.arg("--print").arg("file-names"); From 7fa41800d8418e4f0304e5e29d859388d41a38fe Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 2 Jun 2026 06:24:05 +0000 Subject: [PATCH 08/71] Prepare for merging from rust-lang/rust This updates the rust-version file to 4f84d9fac456d973d592cf3fb48db958ecf22506. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index aaeb424e108f0..31b100ae3467e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -bef8e620f19adbfd1530e916ab8caa296ef9c3ee +4f84d9fac456d973d592cf3fb48db958ecf22506 From 05e3945b2b62a3a9498bece0b62c9465c814ede4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 2 Jun 2026 06:33:26 +0000 Subject: [PATCH 09/71] fmt --- src/tools/miri/tests/fail/validity/fn_arg_never_type.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/fail/validity/fn_arg_never_type.rs b/src/tools/miri/tests/fail/validity/fn_arg_never_type.rs index a6ffb325191b0..04d6597f64c92 100644 --- a/src/tools/miri/tests/fail/validity/fn_arg_never_type.rs +++ b/src/tools/miri/tests/fail/validity/fn_arg_never_type.rs @@ -2,7 +2,8 @@ use std::mem::transmute; enum Never {} -fn foo(x: Never) { //~ERROR: invalid value of type Never +fn foo(x: Never) { + //~^ERROR: invalid value of type Never let ptr = &raw const x; println!("{ptr:p}"); } From 5886a518402b2884681e07e4b2d270bad632b2c4 Mon Sep 17 00:00:00 2001 From: Xiaoyi Shi Date: Wed, 3 Jun 2026 17:00:00 +0800 Subject: [PATCH 10/71] rustc_session: apply -Zremap-cwd-prefix when building the file mapping `-Zremap-cwd-prefix=` was applied inside `parse_remap_path_prefix`, which resolved `std::env::current_dir()` and pushed `(cwd, to)` onto `remap_path_prefix`. That option is `[TRACKED_NO_CRATE_HASH]`, so the absolute working directory entered the incremental command-line-args hash, and building the same sources from a different directory (e.g. a Bazel sandbox, whose path contains a per-action counter) purged the whole incremental cache even though the remapped output is identical. `--remap-path-prefix` and `-Zremap-cwd-prefix` already have separate options (`remap_path_prefix` and `remap_cwd_prefix`). Keep each option holding exactly its own flag, and apply `-Zremap-cwd-prefix` in `file_path_mapping` -- i.e. when building the (untracked) applied `FilePathMapping` -- rather than when parsing/tracking options. The absolute cwd then lives only in the runtime mapping, never in a tracked field. Output is unchanged (the cwd entry is still appended last). Tracking stays sound: the cwd's effect is tracked via `remap_cwd_prefix` (the target prefix baked into output) and `working_dir` (whose `RealFileName` hash includes the real path only when it was not fully remapped, i.e. exactly when the cwd can leak into output). The stable `--remap-path-prefix` flag is unchanged in data and in tracking. A unit test guards against the cwd being merged back into `remap_path_prefix`. --- compiler/rustc_interface/src/tests.rs | 29 +++++++++++- compiler/rustc_session/src/config.rs | 44 ++++++++++++------- .../src/compiler-flags/remap-cwd-prefix.md | 6 +++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0498d835df5f5..f8b63ea8a8f13 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -25,7 +25,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib}; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts}; use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; -use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; +use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, @@ -175,6 +175,33 @@ fn test_can_print_warnings() { }); } +// `-Zremap-cwd-prefix` must not be merged into the tracked `remap_path_prefix` option: +// storing the absolute cwd there invalidates the incremental cache across build +// directories (see #132132). It must instead be applied via `file_path_mapping`. +#[test] +fn test_remap_cwd_prefix_not_in_remap_path_prefix() { + sess_and_cfg( + &["--remap-path-prefix=/explicit=mapped", "-Zremap-cwd-prefix=cwd-mapped"], + |sess, _cfg| { + // The tracked option holds only the explicit `--remap-path-prefix` entry. + assert_eq!( + sess.opts.remap_path_prefix, + vec![(PathBuf::from("/explicit"), PathBuf::from("mapped"))], + ); + + // ... but the cwd remapping is still applied via `file_path_mapping`. + let cwd = std::env::current_dir().unwrap(); + let remapped = sess + .opts + .file_path_mapping() + .to_real_filename(&RealFileName::empty(), cwd.join("foo.rs")) + .path(RemapPathScopeComponents::DEBUGINFO) + .to_path_buf(); + assert_eq!(remapped, PathBuf::from("cwd-mapped/foo.rs")); + }, + ); +} + #[test] fn test_output_types_tracking_hash_different_paths() { let mut v1 = Options::default(); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e82f67eac5e9f..5bcfdada4ec9e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1375,9 +1375,21 @@ pub fn host_tuple() -> &'static str { fn file_path_mapping( remap_path_prefix: Vec<(PathBuf, PathBuf)>, + remap_cwd_prefix: Option<&Path>, remap_path_scope: RemapPathScopeComponents, ) -> FilePathMapping { - FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope) + // Apply `-Zremap-cwd-prefix` here rather than in `parse_remap_path_prefix`, so the + // absolute cwd is never stored in the tracked `remap_path_prefix` option (#132132). + let cwd_remap = if let Some(to) = remap_cwd_prefix + && let Ok(cwd) = std::env::current_dir() + { + Some((cwd, to.to_path_buf())) + } else { + None + }; + // The cwd remapping is appended last: `map_prefix` tries entries in reverse order, so this + // keeps `-Zremap-cwd-prefix` taking precedence over `--remap-path-prefix`, as documented. + FilePathMapping::new(remap_path_prefix.into_iter().chain(cwd_remap).collect(), remap_path_scope) } impl Default for Options { @@ -1389,7 +1401,8 @@ impl Default for Options { // to create a default working directory. let working_dir = { let working_dir = std::env::current_dir().unwrap(); - let file_mapping = file_path_mapping(Vec::new(), RemapPathScopeComponents::empty()); + let file_mapping = + file_path_mapping(Vec::new(), None, RemapPathScopeComponents::empty()); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; @@ -1450,7 +1463,11 @@ impl Options { } pub fn file_path_mapping(&self) -> FilePathMapping { - file_path_mapping(self.remap_path_prefix.clone(), self.remap_path_scope) + file_path_mapping( + self.remap_path_prefix.clone(), + self.unstable_opts.remap_cwd_prefix.as_deref(), + self.remap_path_scope, + ) } /// Returns `true` if there will be an output file generated. @@ -2384,9 +2401,8 @@ pub fn parse_externs( fn parse_remap_path_prefix( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, - unstable_opts: &UnstableOptions, ) -> Vec<(PathBuf, PathBuf)> { - let mut mapping: Vec<(PathBuf, PathBuf)> = matches + matches .opt_strs("remap-path-prefix") .into_iter() .map(|remap| match remap.rsplit_once('=') { @@ -2395,15 +2411,7 @@ fn parse_remap_path_prefix( } Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) - .collect(); - match &unstable_opts.remap_cwd_prefix { - Some(to) => match std::env::current_dir() { - Ok(cwd) => mapping.push((cwd, to.clone())), - Err(_) => (), - }, - None => (), - }; - mapping + .collect() } fn parse_logical_env( @@ -2661,7 +2669,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let externs = parse_externs(early_dcx, matches, &unstable_opts); - let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); + let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches); let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts); let pretty = parse_pretty(early_dcx, &unstable_opts); @@ -2729,7 +2737,11 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M early_dcx.early_fatal(format!("Current directory is invalid: {e}")); }); - let file_mapping = file_path_mapping(remap_path_prefix.clone(), remap_path_scope); + let file_mapping = file_path_mapping( + remap_path_prefix.clone(), + unstable_opts.remap_cwd_prefix.as_deref(), + remap_path_scope, + ); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; diff --git a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md index 3890a12b7e684..bcf526694ad94 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md @@ -16,6 +16,12 @@ directory from build output, while allowing the command line to be universally reproducible, such that the same execution will work on all machines, regardless of build environment. +Unlike passing the equivalent mapping through `--remap-path-prefix`, the current +working directory does not take part in incremental compilation's dependency +tracking. Building the same sources from different directories (for example, a +sandboxed or per-build checkout path) therefore reuses the incremental cache +rather than invalidating it. + ## Example ```sh # This would produce an absolute path to main.rs in build outputs of From 1fc7bf3f0a5b9e4c7638b3c8a415be059f8b72e6 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 4 Jun 2026 06:27:38 +0000 Subject: [PATCH 11/71] Prepare for merging from rust-lang/rust This updates the rust-version file to 76dfce2cb2d3f7b7f34d62e6ffe044f7e7d76948. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 31b100ae3467e..13f350eeb6c5c 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4f84d9fac456d973d592cf3fb48db958ecf22506 +76dfce2cb2d3f7b7f34d62e6ffe044f7e7d76948 From 4799240aaea34edfe0e0e1ed296e05643e3f62ac Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 4 Jun 2026 06:37:11 +0000 Subject: [PATCH 12/71] fmt --- src/tools/miri/src/shims/tls.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index cbfbb4e200a0f..1d87a66bbe461 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -267,7 +267,13 @@ impl<'tcx> TlsDtorsState<'tcx> { let fls_keys_with_dtors = this.lookup_windows_fls_keys_with_dtors()?; // And move to the next state, that runs them. - break 'new_state WindowsDtors(RunningWindowsDtorState { last_key: None, remaining_keys: fls_keys_with_dtors }, dtors); + break 'new_state WindowsDtors( + RunningWindowsDtorState { + last_key: None, + remaining_keys: fls_keys_with_dtors, + }, + dtors, + ); } _ => { // No TLS dtor support. @@ -453,11 +459,11 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Keys without dtors will not be set to zero. // [PflsCallbackFunction's docs]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nc-winnt-pfls_callback_function // [`RtlProcessFlsData`]: https://github.com/wine-mirror/wine/blob/wine-11.0/dlls/ntdll/thread.c#L679 - + // We are done running the previous key's destructor, so set it's value to zero. if let Some(last_key) = state.last_key.take() { if let Some(TlsEntry { data, .. }) = this.machine.tls.keys.get_mut(&last_key) { - data.remove(&active_thread); + data.remove(&active_thread); }; } @@ -470,7 +476,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let (instance, span) = dtor.to_owned(); - + // If the key has no value in this thread, move on to the next key. let ptr = match data.get(&active_thread) { Some(data_scalar) => *data_scalar, @@ -481,7 +487,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ptr.to_target_usize(this).unwrap() != 0, "TLS key's value can't be null (should be absent instead)" ); - + trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); // We'll clear this key's value next time we are called. @@ -497,7 +503,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Poll::Pending); } - + // We are done scheduling all the keys. interp_ok(Poll::Ready(())) } From 07fe0fac666885c1aef5f268dadaf2a37769462d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 4 Jun 2026 08:44:57 +0200 Subject: [PATCH 13/71] clippy --- src/tools/miri/src/shims/tls.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 1d87a66bbe461..98436f2eac3be 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -470,18 +470,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { while let Some(key) = state.remaining_keys.pop_front() { // Fetch dtor for this `key`. // If the key doesn't have a dtor or does not exist any more, move on to the next key. - let (data, dtor) = match this.machine.tls.keys.get(&key) { - Some(TlsEntry { data, dtor: Some(dtor) }) => (data, dtor), - _ => continue, + let Some(TlsEntry { data, dtor: Some(dtor) }) = this.machine.tls.keys.get(&key) else { + continue; }; let (instance, span) = dtor.to_owned(); // If the key has no value in this thread, move on to the next key. - let ptr = match data.get(&active_thread) { - Some(data_scalar) => *data_scalar, - None => continue, - }; + let Some(&ptr) = data.get(&active_thread) else { continue }; assert!( ptr.to_target_usize(this).unwrap() != 0, From 566e318d0292cd5ca8eeda6f0fc254831174aa54 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 2 Jun 2026 22:40:46 +0200 Subject: [PATCH 14/71] CreateFileW: retry to deal with race conditions --- src/tools/miri/src/shims/windows/fs.rs | 227 +++++++++++++++---------- 1 file changed, 137 insertions(+), 90 deletions(-) diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index de0ad7ffa5edd..6c5610ea07705 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -157,29 +157,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("CreateFileW: Template files are not supported"); } - // We need to know if the file is a directory to correctly open directory handles. This is - // racy, but currently the stdlib doesn't appear to offer a better solution. We do later - // verify that our guess was correct so worst-case, Miri ICEs here. - // FIXME: retry in a loop if we get an error indicating we got the wrong file type? - let is_dir = file_name.is_dir(); - - // BACKUP_SEMANTICS is how Windows calls the act of opening a directory handle. - if !attributes.contains(FileAttributes::BACKUP_SEMANTICS) && is_dir { - this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; - return interp_ok(Handle::Invalid); - } - - let desired_read = desired_access & generic_read != 0; - let desired_write = desired_access & generic_write != 0; - - let mut options = fs::OpenOptions::new(); - if desired_read { + // Parse desired_access + let mut desired_read = false; + if desired_access & generic_read != 0 { + desired_read = true; desired_access &= !generic_read; - options.read(true); } - if desired_write { + let mut desired_write = false; + if desired_access & generic_write != 0 { + desired_write = true; desired_access &= !generic_write; - options.write(true); } if desired_access != 0 { @@ -188,90 +175,150 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - // Per the documentation: - // If the specified file exists and is writable, the function truncates the file, - // the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS. - // If the specified file does not exist and is a valid path, a new file is created, - // the function succeeds, and the last-error code is set to zero. - // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew - // - // This is racy, but there doesn't appear to be an std API that both succeeds if a - // file exists but tells us it isn't new. Either we accept racing one way or another, - // or we use an iffy heuristic like file creation time. This implementation prefers - // to fail in the direction of erroring more often. - if let CreateAlways | OpenAlways = creation_disposition - && file_name.exists() - { - this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; - } + // We start a retry loop to deal with the `is_dir` and `exists_already` race, see below. + // We add a retry counter to avoid infinite loops when things go wrong. + let mut counter = 0u32; + loop { + if counter >= 100 { + panic!( + "CreateFileW seems stuck in an infinite retry loop. \ + If you can reproduce this, please file a bug." + ); + } + counter = counter.strict_add(1); + + // We need to know if the file is a directory to correctly open directory handles. + // The standard library only lets us open something as a file or a directory, so + // we check for that and then retry if we end up with the wrong thing. + let is_dir = file_name.is_dir(); - let handle = if is_dir { - // Open this as a directory. - // FIXME: shouldn't we check `creation_disposition` here? - Dir::open(&file_name).map(|dir| { + // BACKUP_SEMANTICS is how Windows calls the act of opening a directory handle. + if !attributes.contains(FileAttributes::BACKUP_SEMANTICS) && is_dir { + this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; + return interp_ok(Handle::Invalid); + } + + if is_dir { + // Open this as a directory. + // FIXME: shouldn't we check `creation_disposition` here? We do know that it already + // exists. + let dir = match Dir::open(&file_name) { + Ok(dir) => dir, + Err(e) => { + if e.kind() == io::ErrorKind::NotADirectory { + // This changed from a directory to a file. Retry. + continue; + } + this.set_last_error(e)?; + return interp_ok(Handle::Invalid); + } + }; #[cfg(not(bootstrap))] - assert!( - dir.metadata().unwrap().is_dir(), - "we tried to open a directory and got a file" - ); + if !dir.metadata().unwrap().is_dir() { + // This changed from a directory to a file. Retry. + continue; + } + + // Windows communicates information via the error code on success. + if let CreateAlways | OpenAlways = creation_disposition { + this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; + } + let fd_num = this.machine.fds.insert_new(DirHandle { dir, path: file_name }); - Handle::File(fd_num) - }) - } else { - // Open this as a standard file. We already set the `read`/`write` flags above, - // but we still need to represent the `creation_disposition`. - match creation_disposition { - CreateAlways | OpenAlways => { - options.create(true); - if creation_disposition == CreateAlways { - options.truncate(true); + return interp_ok(Handle::File(fd_num)); + } else { + // Per the documentation: + // If the specified file exists and is writable, the function truncates the file, + // the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS. + // If the specified file does not exist and is a valid path, a new file is created, + // the function succeeds, and the last-error code is set to zero. + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew + // + // We check whether it exists before trying to open it. This is racy, but there + // doesn't appear to be an std API that both succeeds whether or not a file already + // exists and tells us whether it is new. So instead we will open the file in a way + // that we can verify whether our guess is correct, and retry if it is not. + let exists_already = file_name.exists(); + + // Open this as a standard file. + let mut options = fs::OpenOptions::new(); + options.read(desired_read); + options.write(desired_write); + match creation_disposition { + CreateAlways | OpenAlways => { + // We verify `exists_already`: if we expect it to already exist, we set no + // flag, thus failing if it doesn't exist. If we expect the file to not + // exist, we use `create_new` to fail if it does exist. + if !exists_already { + options.create_new(true); + } + if creation_disposition == CreateAlways { + options.truncate(true); + } } - } - CreateNew => { - options.create_new(true); - // Per `create_new` documentation: - // The file must be opened with write or append access in order to create a new file. - // https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new - if !desired_write { - options.append(true); + CreateNew => { + options.create_new(true); + // Per `create_new` documentation: + // The file must be opened with write or append access in order to create a new file. + // https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new + if !desired_write { + options.append(true); + } } - } - OpenExisting => { - if !desired_read && !desired_write { - // Windows supports handles with no permissions. These allow things such as - // reading metadata, but not file content. This is used by `Path::metadata`. - // `std` does not support this. To ensure we behave correctly as often as - // possible, we open the file for reading and live with the fact that this - // might incorrectly return `PermissionDenied`. - // FIXME: We could probably use `OpenOptionsExt`? On a Unix host, - // `O_PATH` apparently can open files for metadata use only. - options.read(true); + OpenExisting => { + if !desired_read && !desired_write { + // Windows supports handles with no permissions. These allow things such as + // reading metadata, but not file content. This is used by `Path::metadata`. + // `std` does not support this. To ensure we behave correctly as often as + // possible, we open the file for reading and live with the fact that this + // might incorrectly return `PermissionDenied`. + // FIXME: We could probably use `OpenOptionsExt`? On a Unix host, + // `O_PATH` apparently can open files for metadata use only. + options.read(true); + } + } + TruncateExisting => { + options.truncate(true); } } - TruncateExisting => { - options.truncate(true); + + let file = match options.open(&file_name) { + Ok(file) => file, + Err(e) => { + let kind = e.kind(); + if kind == io::ErrorKind::IsADirectory { + // This changed from a file to a directory. Retry. + continue; + } + if exists_already && kind == io::ErrorKind::NotFound { + // The file disappeared. Retry. + continue; + } + if !exists_already && kind == io::ErrorKind::AlreadyExists { + // The file got created by something else. Retry. + continue; + } + this.set_last_error(e)?; + return interp_ok(Handle::Invalid); + } + }; + if file.metadata().unwrap().is_dir() { + // This changed from a file to a directory. Retry. + continue; } - } - options.open(file_name).map(|file| { - assert!( - !file.metadata().unwrap().is_dir(), - "we tried to open a file and got a directory" - ); + // Windows communicates information via the error code on success. + if let CreateAlways | OpenAlways = creation_disposition + && exists_already + { + this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; + } let fd_num = this.machine.fds.insert_new(FileHandle { file, writable: desired_write, readable: desired_read, }); - Handle::File(fd_num) - }) - }; - - match handle { - Ok(handle) => interp_ok(handle), - Err(e) => { - this.set_last_error(e)?; - interp_ok(Handle::Invalid) + return interp_ok(Handle::File(fd_num)); } } } From 3d697650f286f2bb0fd702b8a64434dfccd397b4 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 4 Jun 2026 03:44:31 -0400 Subject: [PATCH 15/71] refactor: delegate raw ref mut checks to a new helper method --- compiler/rustc_parse/src/parser/diagnostics.rs | 13 ++++++++----- compiler/rustc_parse/src/parser/expr.rs | 7 +------ compiler/rustc_parse/src/parser/mod.rs | 6 +----- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 1360dc23a78f2..fbceb185c190c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -752,16 +752,19 @@ impl<'a> Parser<'a> { Err(err) } + pub(super) fn is_expected_raw_ref_mut(&self) -> bool { + self.prev_token.is_keyword(kw::Raw) + && self.expected_token_types.contains(TokenType::KwMut) + && self.expected_token_types.contains(TokenType::KwConst) + && self.token.can_begin_expr() + } + /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`. /// /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this /// label may need added to other diagnostics emission paths as needed. pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) { - if self.prev_token.is_keyword(kw::Raw) - && self.expected_token_types.contains(TokenType::KwMut) - && self.expected_token_types.contains(TokenType::KwConst) - && self.token.can_begin_expr() - { + if self.is_expected_raw_ref_mut() { err.span_suggestions( self.prev_token.span.shrink_to_hi(), "`&raw` must be followed by `const` or `mut` to be a raw reference expression", diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 43040b9cef6aa..1037fd0c5953a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1281,12 +1281,7 @@ impl<'a> Parser<'a> { let seq = match self.parse_expr_paren_seq() { Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))), - Err(err) - if self.prev_token.is_keyword(kw::Raw) - && self.expected_token_types.contains(TokenType::KwMut) - && self.expected_token_types.contains(TokenType::KwConst) - && self.token.can_begin_expr() => - { + Err(err) if self.is_expected_raw_ref_mut() => { let err_span = self.prev_token.span.to(self.token.span); let guar = err.emit(); // Preserve the call expression so later passes can still diagnose the callee, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ecdf242e18321..64255d51a7c69 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -920,11 +920,7 @@ impl<'a> Parser<'a> { // `&raw ` already has a specific suggestion for missing // `const`/`mut`, so don't recover `` as the next element in // a comma-separated list. - if exp.token_type == TokenType::Comma - && self.prev_token.is_keyword(kw::Raw) - && self.expected_token_types.contains(TokenType::KwMut) - && self.expected_token_types.contains(TokenType::KwConst) - && self.token.can_begin_expr() + if exp.token_type == TokenType::Comma && self.is_expected_raw_ref_mut() { return Err(expect_err); } From 5fde1fdac9a8f7a292549540bda132d2c7a51f12 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 4 Jun 2026 04:03:33 -0400 Subject: [PATCH 16/71] preserve arg count in call recovery --- compiler/rustc_parse/src/parser/expr.rs | 29 +++++++++++++++---- .../recover/raw-no-const-mut-arg-list.rs | 3 ++ .../recover/raw-no-const-mut-arg-list.stderr | 17 +++++++++-- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1037fd0c5953a..781f2c21828dc 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -29,7 +29,7 @@ use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; -use super::diagnostics::{ConsumeClosingDelim, SnapshotParser}; +use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ @@ -1286,10 +1286,29 @@ impl<'a> Parser<'a> { let guar = err.emit(); // Preserve the call expression so later passes can still diagnose the callee, // while treating the malformed `&raw ` argument as an error expression. - self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::Yes); - let err_arg = self.mk_expr_err(err_span, guar); - return self - .mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, thin_vec![err_arg])); + let mut args = thin_vec![self.mk_expr_err(err_span, guar)]; + let mut depth = 0usize; + loop { + if self.token == token::Eof { + break; + } else if depth == 0 && self.check(exp!(CloseParen)) { + self.bump(); + break; + } else if depth == 0 && self.check(exp!(Comma)) { + let comma_span = self.token.span; + self.bump(); + if !self.check(exp!(CloseParen)) { + args.push(self.mk_expr_err(comma_span.shrink_to_hi(), guar)); + } + continue; + } else if self.token.kind.open_delim().is_some() { + depth += 1; + } else if self.token.kind.close_delim().is_some() && depth > 0 { + depth -= 1; + } + self.bump(); + } + return self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)); } Err(err) => Err(err), }; diff --git a/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs b/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs index db47140b85e42..7ed740cb02804 100644 --- a/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs +++ b/tests/ui/parser/recover/raw-no-const-mut-arg-list.rs @@ -1,9 +1,12 @@ // Regression test for https://github.com/rust-lang/rust/issues/157015. fn takes_raw_ptr(_: *const u32) {} +fn takes_raw_ptr_args(_: *const u32, _: *const u32) {} fn main() { let x = 0u32; takes_raw_ptr(&raw x); //~^ ERROR expected one of + takes_raw_ptr_args(&raw x, &raw x); + //~^ ERROR expected one of } diff --git a/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr b/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr index e580497e027a8..3bb6fe317a6f7 100644 --- a/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr +++ b/tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` - --> $DIR/raw-no-const-mut-arg-list.rs:7:24 + --> $DIR/raw-no-const-mut-arg-list.rs:8:24 | LL | takes_raw_ptr(&raw x); | ^ expected one of 10 possible tokens @@ -11,5 +11,18 @@ LL | takes_raw_ptr(&raw const x); LL | takes_raw_ptr(&raw mut x); | +++ -error: aborting due to 1 previous error +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` + --> $DIR/raw-no-const-mut-arg-list.rs:10:29 + | +LL | takes_raw_ptr_args(&raw x, &raw x); + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | takes_raw_ptr_args(&raw const x, &raw x); + | +++++ +LL | takes_raw_ptr_args(&raw mut x, &raw x); + | +++ + +error: aborting due to 2 previous errors From a832c146f09a1f2d24774e7e656b435840ef8937 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Wed, 3 Jun 2026 23:02:35 +0200 Subject: [PATCH 17/71] Add powerpc64-unknown-linux-gnuelfv2 target This is virtually the same target as the existing -gnu target, but using the ELFv2 ABI instead of the ELFv1 ABI and made possible now that we expose target_abi = "elfv1" or "elfv2" on the 64-bit PowerPC targets. --- compiler/rustc_codegen_llvm/src/context.rs | 8 +++ compiler/rustc_target/src/spec/mod.rs | 1 + .../powerpc64_unknown_linux_gnuelfv2.rs | 30 +++++++++++ src/bootstrap/src/core/sanity.rs | 1 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../powerpc64-unknown-linux-gnuelfv2.md | 50 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 ++ 8 files changed, 95 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs create mode 100644 src/doc/rustc/src/platform-support/powerpc64-unknown-linux-gnuelfv2.md diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 85a7f9cab73a4..2b4d82f61d0e5 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -201,6 +201,14 @@ pub(crate) unsafe fn create_module<'ll>( if sess.target.arch == Arch::PowerPC64 { // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673 target_data_layout = target_data_layout.replace("-f64:32:64", ""); + + // LLVM 22 fixed the data layout calculation for targets that default to ELFv1 + // when the ABI is set to ELFv2. With LLVM 21, the ELFv1 datalayout must be used, + // which will overalign function entries. + // https://github.com/llvm/llvm-project/pull/149725 + if sess.target.llvm_target == "powerpc64-unknown-linux-gnu" { + target_data_layout = target_data_layout.replace("-Fn32", "-Fi64"); + } } if sess.target.arch == Arch::AmdGpu { // LLVM 22 specified ELF mangling in the amdgpu data layout: diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ea6abe5491262..3000242e94464 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1465,6 +1465,7 @@ supported_targets! { ("powerpc-unknown-linux-muslspe", powerpc_unknown_linux_muslspe), ("powerpc64-ibm-aix", powerpc64_ibm_aix), ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), + ("powerpc64-unknown-linux-gnuelfv2", powerpc64_unknown_linux_gnuelfv2), ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl), ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs new file mode 100644 index 0000000000000..c7392a1b90b57 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs @@ -0,0 +1,30 @@ +use rustc_abi::Endian; + +use crate::spec::{ + Arch, Cc, CfgAbi, LinkerFlavor, Lld, LlvmAbi, StackProbeType, Target, TargetMetadata, + TargetOptions, base, +}; + +pub(crate) fn target() -> Target { + let mut base = base::linux_gnu::opts(); + base.cpu = "ppc64".into(); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); + base.max_atomic_width = Some(64); + base.stack_probes = StackProbeType::Inline; + base.cfg_abi = CfgAbi::ElfV2; + base.llvm_abiname = LlvmAbi::ElfV2; + + Target { + llvm_target: "powerpc64-unknown-linux-gnu".into(), + metadata: TargetMetadata { + description: Some("PPC64 Linux (ELFv2 ABI, kernel 3.2, glibc 2.17)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), + arch: Arch::PowerPC64, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, + } +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index ca8af279b92bc..3c2348c9bf71c 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -37,6 +37,7 @@ pub struct Finder { /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "powerpc64-unknown-linux-gnuelfv2", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index cc10f476780c5..af8060adcc262 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -107,6 +107,7 @@ - [powerpc-unknown-linux-gnuspe](platform-support/powerpc-unknown-linux-gnuspe.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) + - [powerpc64-unknown-linux-gnuelfv2](platform-support/powerpc64-unknown-linux-gnuelfv2.md) - [powerpc64-unknown-linux-musl](platform-support/powerpc64-unknown-linux-musl.md) - [powerpc64le-unknown-linux-gnu](platform-support/powerpc64le-unknown-linux-gnu.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7cbfb8dd11345..56ff5ba70c097 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -385,6 +385,7 @@ target | std | host | notes [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) [`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2) +[`powerpc64-unknown-linux-gnuelfv2`](platform-support/powerpc64-unknown-linux-gnuelfv2.md) | ✓ | ✓ | PPC64 Linux (ELFv2 ABI, kernel 3.2, glibc 2.17) [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD diff --git a/src/doc/rustc/src/platform-support/powerpc64-unknown-linux-gnuelfv2.md b/src/doc/rustc/src/platform-support/powerpc64-unknown-linux-gnuelfv2.md new file mode 100644 index 0000000000000..59ae4507de302 --- /dev/null +++ b/src/doc/rustc/src/platform-support/powerpc64-unknown-linux-gnuelfv2.md @@ -0,0 +1,50 @@ +# powerpc64-unknown-linux-gnuelfv2 + +**Tier: 3** + +Target for 64-bit big endian PowerPC Linux programs using the ELFv2 ABI and +the GNU C library. + +## Target maintainers + +[@Gelbpunkt](https://github.com/Gelbpunkt) + +## Requirements + +Building the target itself requires a 64-bit big endian PowerPC compiler that +uses the ELFv2 ABI and is supported by `cc-rs`. + +## Building the target + +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["powerpc64-unknown-linux-gnuelfv2"] +``` + +Make sure your C compiler is included in `$PATH`, then add it to the +`bootstrap.toml`: + +```toml +[target.powerpc64-unknown-linux-gnuelfv2] +cc = "powerpc64-linux-gnu-gcc" +cxx = "powerpc64-linux-gnu-g++" +ar = "powerpc64-linux-gnu-ar" +linker = "powerpc64-linux-gnu-gcc" +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will first need to build Rust with the target enabled (see +"Building the target" above). + +## Cross-compilation + +This target can be cross-compiled from any host. + +## Testing + +This target can be tested as normal with `x.py` on a 64-bit big endian PowerPC +host or via QEMU emulation. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index ce6629b4f27ce..7cc7cb037d4ce 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -394,6 +394,9 @@ //@ revisions: powerpc64_unknown_linux_gnu //@ [powerpc64_unknown_linux_gnu] compile-flags: --target powerpc64-unknown-linux-gnu //@ [powerpc64_unknown_linux_gnu] needs-llvm-components: powerpc +//@ revisions: powerpc64_unknown_linux_gnuelfv2 +//@ [powerpc64_unknown_linux_gnuelfv2] compile-flags: --target powerpc64-unknown-linux-gnuelfv2 +//@ [powerpc64_unknown_linux_gnuelfv2] needs-llvm-components: powerpc //@ revisions: powerpc64_unknown_linux_musl //@ [powerpc64_unknown_linux_musl] compile-flags: --target powerpc64-unknown-linux-musl //@ [powerpc64_unknown_linux_musl] needs-llvm-components: powerpc From 553d694e47d881196cf6cc25edc8e27b1e3c9599 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 5 Jun 2026 06:14:58 +0000 Subject: [PATCH 18/71] Prepare for merging from rust-lang/rust This updates the rust-version file to 3179a47d67719a92b4d3c3689aca391c40ff1a04. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 13f350eeb6c5c..61262d3c6d418 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -76dfce2cb2d3f7b7f34d62e6ffe044f7e7d76948 +3179a47d67719a92b4d3c3689aca391c40ff1a04 From 9c189a48957aabbf8903047286804244731fa360 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 08:29:10 +0200 Subject: [PATCH 19/71] move cron job earlier --- src/tools/miri/.github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 73a6d9026e9c4..294c8dd3d5aa1 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: branches: - 'master' schedule: - - cron: '44 4 * * *' # At 4:44 UTC every day. + - cron: '14 4 * * *' # At 4:14 UTC every day. defaults: run: From 7e64f8a62a0caf3130a3150e126ef9c8ff0cdb3c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 08:49:19 +0200 Subject: [PATCH 20/71] bump 'aes' --- src/tools/miri/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index bebacba77daf9..00794371d50c6 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -19,9 +19,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" dependencies = [ "cipher", "cpubits", From 819843c64af392427dc2e7b4b27936f4acea8fb3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 15:17:22 +0200 Subject: [PATCH 21/71] add missing FIXME for Windows TLS API tests --- src/tools/miri/src/shims/windows/foreign_items.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index f7feeb4cfde14..efef55f5cf91b 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -638,6 +638,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "TlsAlloc" => { + // FIXME: This does not have a direct test (#3179). // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. @@ -651,6 +652,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( shim_sig!(extern "system" fn(u32) -> *mut _), link_name, @@ -663,6 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(ptr, dest)?; } "TlsSetValue" => { + // FIXME: This does not have a direct test (#3179). let [key, new_ptr] = this.check_shim_sig( shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), link_name, @@ -678,6 +681,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "TlsFree" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( shim_sig!(extern "system" fn(u32) -> winapi::BOOL), link_name, @@ -693,6 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Fiber-local storage - similar to TLS but supports destructors. "FlsAlloc" => { + // FIXME: This does not have a direct test (#3179). // Create key and return it. let [dtor] = this.check_shim_sig( shim_sig!(extern "system" fn(winapi::PFLS_CALLBACK_FUNCTION) -> u32), @@ -716,6 +721,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "FlsGetValue" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( shim_sig!(extern "system" fn(u32) -> *mut _), link_name, @@ -728,6 +734,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(ptr, dest)?; } "FlsSetValue" => { + // FIXME: This does not have a direct test (#3179). let [key, new_ptr] = this.check_shim_sig( shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), link_name, @@ -743,6 +750,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "FlsFree" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( shim_sig!(extern "system" fn(u32) -> winapi::BOOL), link_name, @@ -763,6 +771,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "IsThreadAFiber" => { + // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig( shim_sig!(extern "system" fn() -> winapi::BOOL), link_name, From 4619a892c5b6578df1cb2d8eada3d9077b836804 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 15:17:39 +0200 Subject: [PATCH 22/71] use Size::unsigned_int_max to make code more readable --- src/tools/miri/src/shims/tls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 98436f2eac3be..5767add2b9450 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -63,19 +63,19 @@ impl<'tcx> Default for TlsData<'tcx> { impl<'tcx> TlsData<'tcx> { /// Generate a new TLS key with the given destructor. - /// `max_size` determines the integer size the key has to fit in. + /// `key_size` determines the integer size the key has to fit in. #[expect(clippy::arithmetic_side_effects)] pub fn create_tls_key( &mut self, dtor: Option<(ty::Instance<'tcx>, Span)>, - max_size: Size, + key_size: Size, ) -> InterpResult<'tcx, TlsKey> { let new_key = self.next_key; self.next_key += 1; self.keys.try_insert(new_key, TlsEntry { data: Default::default(), dtor }).unwrap(); trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); - if max_size.bits() < 128 && new_key >= (1u128 << max_size.bits()) { + if new_key > key_size.unsigned_int_max() { throw_unsup_format!("we ran out of TLS key space"); } interp_ok(new_key) From 6d2f2bed67d090464fa4db0ab366e4078b8618ad Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2026 12:23:42 +0200 Subject: [PATCH 23/71] add Miri sticker to README --- src/tools/miri/README.md | 4 ++++ src/tools/miri/miri-sticker.png | Bin 0 -> 192132 bytes 2 files changed, 4 insertions(+) create mode 100644 src/tools/miri/miri-sticker.png diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 550a2e25140ef..6b8d8ed1f8018 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -1,5 +1,9 @@ # Miri +Corro, the Unsafe Rusturchin, drinking from a juice bottle labeled 'Miri - 100% safe' + Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For instance: diff --git a/src/tools/miri/miri-sticker.png b/src/tools/miri/miri-sticker.png new file mode 100644 index 0000000000000000000000000000000000000000..9bb282af1b00ab61746ecdc8a4609846f6c0d93c GIT binary patch literal 192132 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

6vbHG424$`|46OKRaF&qi^o$Q;|I@HRB|> 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 z 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

Y85OQ3TzVk3ur?(N@x%(aprxTUd|D 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 Date: Sun, 7 Jun 2026 10:11:40 +0200 Subject: [PATCH 24/71] fix spurious epoll-blocking test --- .../tests/pass-dep/libc/libc-epoll-blocking.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index a61fdbf184498..05f1b8ae660a4 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -1,11 +1,11 @@ //@only-target: linux android illumos // test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule. -//@compile-flags: -Zmiri-deterministic-concurrency //@revisions: edge_triggered level_triggered //@run-native use std::convert::TryInto; use std::thread; +use std::time::Duration; #[path = "../../utils/libc.rs"] mod libc_utils; @@ -72,8 +72,8 @@ fn test_epoll_block_then_unblock() { check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: fds[0] }], -1); let thread1 = thread::spawn(move || { - thread::yield_now(); - // Due to deterministic concurrency, we'll only get here when the other thread blocks. + thread::sleep(Duration::from_millis(10)); + // Since we slept, we should only get here after the other thread blocks. write_all(fds[1], b"abcde").unwrap(); }); @@ -81,7 +81,7 @@ fn test_epoll_block_then_unblock() { // Edge-triggered epoll will block until the write succeeds and the buffer // becomes readable. This is because we already read the writable edge // before so at the time of calling `epoll_wait` there is no active readiness. - check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[0] }], 10); + check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[0] }], 100); } else { // Level-triggered epoll won't wait for the write to succeed because // _some_ readiness is already set (in this case the EPOLLOUT). @@ -143,7 +143,7 @@ fn test_epoll_race() { // Write to the eventfd instance. write_all(fd, &1_u64.to_ne_bytes()).unwrap(); }); - thread::yield_now(); + thread::sleep(Duration::from_millis(10)); // epoll_wait for EPOLLIN. check_epoll_wait(epfd, &[Ev { events: EPOLLIN, data: fd }], -1); // Read from the static mut variable. @@ -169,7 +169,7 @@ fn wakeup_on_new_interest() { check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[1] }], -1); }); // Ensure the thread is blocked. - std::thread::yield_now(); + thread::sleep(Duration::from_millis(10)); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLRDHUP (and EPOLLET if we're in the // `edge_triggered` revision). @@ -213,7 +213,7 @@ fn multiple_events_wake_multiple_threads() { Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() } }); // Yield so both threads are waiting now. - thread::yield_now(); + thread::sleep(Duration::from_millis(10)); // Trigger the eventfd. This triggers two events at once! write_all(fd1, &0_u64.to_ne_bytes()).unwrap(); From 56a344244a162e73b501d847992d157812eeb1f9 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 30 May 2026 13:05:02 +0300 Subject: [PATCH 25/71] [Priroda] Add quit command --- src/tools/miri/priroda/src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 775f2403bd1f0..102b12f794687 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -112,17 +112,20 @@ impl<'tcx> PrirodaContext<'tcx> { fn run_command(&mut self, command: SessionCommand) -> InterpResult<'tcx> { match command { SessionCommand::Step => self.step(), + SessionCommand::Quit => unreachable!("quit is handled by the CLI loop"), } } } enum SessionCommand { Step, + Quit, } fn parse_command(input: &str) -> Option { match input.trim() { "" | "s" | "step" => Some(SessionCommand::Step), + "q" | "quit" => Some(SessionCommand::Quit), _ => None, } } @@ -138,8 +141,17 @@ fn run_cli_loop<'tcx>(session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> io::stdin().read_line(&mut input).unwrap(); if let Some(command) = parse_command(&input) { - session.run_command(command)?; - session.print_location(); + match command { + SessionCommand::Quit => { + println!("quitting"); + return interp_ok(()); + } + + command => { + session.run_command(command)?; + session.print_location(); + } + } } else { println!("no command"); } From 72d02f0ba1f4e1c7a9248eeb60a60aeede6a70b1 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 30 May 2026 14:20:23 +0300 Subject: [PATCH 26/71] [Priroda] Handle EOF explicitly --- src/tools/miri/priroda/src/main.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 102b12f794687..50ad0e761cf0a 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -136,9 +136,12 @@ fn run_cli_loop<'tcx>(session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> io::stdout().flush().unwrap(); let mut input = String::new(); - // TODO: handle EOF explicitly so scripted input can stop the CLI instead - // of being treated like an empty Enter step. - io::stdin().read_line(&mut input).unwrap(); + let bytes_read = io::stdin().read_line(&mut input).unwrap(); + + if bytes_read == 0 { + println!("stdin closed, stopping"); + return interp_ok(()); + } if let Some(command) = parse_command(&input) { match command { From e32687c04bb323a1927ea986b18e7980e458cbad Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 30 May 2026 14:20:40 +0300 Subject: [PATCH 27/71] [Priroda] Report program exit code --- src/tools/miri/priroda/src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 50ad0e761cf0a..80c251e13d33c 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -69,7 +69,10 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { Ok(()) => {} Err(err) => if let Some((return_code, _leak_check)) = report_result(&session.ecx, err) { - //TODO: print the evaluated program's exit code and return to the debugger prompt instead of exiting Priroda. + // TODO: translate Miri termination into a Priroda execution-state enum so + // the CLI loop can distinguish whole-program exit from individual thread + // completion, print the exit code, and return to the debugger prompt. + println!("program finished with exit code {return_code}"); if return_code != 0 { std::process::exit(return_code); } From 375b175a1df0a943c811ce69042d35c53a046b48 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 30 May 2026 14:21:01 +0300 Subject: [PATCH 28/71] [Priroda] Add continue command --- src/tools/miri/priroda/src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 80c251e13d33c..24d90e7d2ab32 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -104,6 +104,13 @@ impl<'tcx> PrirodaContext<'tcx> { self.ecx.miri_step() } + pub fn continue_execution(&mut self) -> InterpResult<'tcx> { + // TODO: stop when execution reaches a breakpoint. + loop { + self.step()?; + } + } + pub fn print_location(&self) { let span = self.ecx.machine.current_user_relevant_span(); let location = self.ecx.tcx.sess.source_map().span_to_diagnostic_string(span); @@ -116,6 +123,7 @@ impl<'tcx> PrirodaContext<'tcx> { match command { SessionCommand::Step => self.step(), SessionCommand::Quit => unreachable!("quit is handled by the CLI loop"), + SessionCommand::Continue => self.continue_execution(), } } } @@ -123,12 +131,14 @@ impl<'tcx> PrirodaContext<'tcx> { enum SessionCommand { Step, Quit, + Continue, } fn parse_command(input: &str) -> Option { match input.trim() { "" | "s" | "step" => Some(SessionCommand::Step), "q" | "quit" => Some(SessionCommand::Quit), + "c" | "continue" => Some(SessionCommand::Continue), _ => None, } } From e24b2465b1b120766a9547ae7585c35b3983f906 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 30 May 2026 22:48:01 +0300 Subject: [PATCH 29/71] [Priroda] Add structured source locations --- src/tools/miri/priroda/src/main.rs | 42 ++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 24d90e7d2ab32..ae21ffad1f1fb 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -12,6 +12,7 @@ extern crate rustc_middle; extern crate rustc_session; use std::io::{self, Write}; +use std::path::PathBuf; use miri::*; use rustc_driver::Compilation; @@ -89,19 +90,32 @@ fn create_ecx<'tcx>(tcx: TyCtxt<'tcx>) -> MiriInterpCx<'tcx> { miri::create_ecx(tcx, entry_id, entry_type, &config, None).unwrap() } +struct SourceLocation { + local_path: Option, + display: String, + line: usize, + column: usize, +} + pub struct PrirodaContext<'tcx> { ecx: MiriInterpCx<'tcx>, + current_location: Option, + last_location: Option, } impl<'tcx> PrirodaContext<'tcx> { fn new(ecx: MiriInterpCx<'tcx>) -> Self { - Self { ecx } + Self { ecx, current_location: None, last_location: None } } // TODO: return a StepResult enum once we distinguish breakpoint stops, // program exit, and other debugger states. pub fn step(&mut self) -> InterpResult<'tcx> { - self.ecx.miri_step() + // state inspection should happen only after a successful step + self.ecx.miri_step()?; + self.last_location = self.current_location.take(); + self.current_location = self.resolve_current_location(); + interp_ok(()) } pub fn continue_execution(&mut self) -> InterpResult<'tcx> { @@ -111,12 +125,30 @@ impl<'tcx> PrirodaContext<'tcx> { } } - pub fn print_location(&self) { + fn resolve_current_location(&self) -> Option { let span = self.ecx.machine.current_user_relevant_span(); - let location = self.ecx.tcx.sess.source_map().span_to_diagnostic_string(span); + if span.is_dummy() { + return None; + } + + let source_map = self.ecx.tcx.sess.source_map(); + let loc = source_map.lookup_char_pos(span.lo()); + + Some(SourceLocation { + local_path: loc.file.name.clone().into_local_path(), + display: source_map.span_to_diagnostic_string(span), + line: loc.line, + column: loc.col_display + 1, + }) + } + + pub fn print_location(&self) { // TODO: skip noisy std/runtime spans and avoid printing `no-location` // once the basic command loop is solid. - println!("{location}"); + match &self.current_location { + Some(location) => println!("{}", location.display), + None => println!("no-location"), + } io::stdout().flush().unwrap(); } fn run_command(&mut self, command: SessionCommand) -> InterpResult<'tcx> { From 0f5355f227ac28cecd5441c0b3c2e54cf266c8e2 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 31 May 2026 04:10:17 +0300 Subject: [PATCH 30/71] [Priroda] Add breakpoint command --- src/tools/miri/priroda/README.md | 24 ++++++++++-- src/tools/miri/priroda/src/main.rs | 61 ++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 246fa93fbc44f..4077e07bcce67 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -1,13 +1,13 @@ # Priroda -Priroda is a step-through debugger for Rust programs running under -Miri. +Priroda is a step-through debugger for Rust programs running under Miri. Current focus: - simple CLI prototype - single-threaded stepping with Miri's interpreter -- commands: empty Enter, `s`, or `step` +- source-location output after stepping +- source-location breakpoint prototype ## Setup @@ -34,4 +34,20 @@ Priroda currently reads `MIRI_SYSROOT` directly. After setup: cargo run -p priroda -- tests/pass/empty_main.rs ``` -At the prompt, press Enter or type `s` / `step`. +## Commands + +| Command | Description | +|---|---| +| Enter, `s`, `step` | Execute one Miri interpreter step. | +| `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | +| `b :`, `break :` | Add a source-location breakpoint. | +| `q`, `quit` | Exit Priroda. | + +EOF also exits Priroda cleanly. + +Example: + +```text +(priroda) break tests/pass/empty_main.rs:3 +(priroda) continue +``` diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index ae21ffad1f1fb..43418d0a03e4b 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -11,6 +11,7 @@ extern crate rustc_log; extern crate rustc_middle; extern crate rustc_session; +use std::collections::HashSet; use std::io::{self, Write}; use std::path::PathBuf; @@ -97,15 +98,26 @@ struct SourceLocation { column: usize, } +#[derive(Eq, Hash, PartialEq)] +struct Breakpoint { + path: PathBuf, + line: usize, +} + pub struct PrirodaContext<'tcx> { ecx: MiriInterpCx<'tcx>, + breakpoints: HashSet, current_location: Option, last_location: Option, } +fn normalize_path(path: PathBuf) -> PathBuf { + path.canonicalize().unwrap_or(path) +} + impl<'tcx> PrirodaContext<'tcx> { fn new(ecx: MiriInterpCx<'tcx>) -> Self { - Self { ecx, current_location: None, last_location: None } + Self { ecx, breakpoints: HashSet::new(), current_location: None, last_location: None } } // TODO: return a StepResult enum once we distinguish breakpoint stops, @@ -119,13 +131,33 @@ impl<'tcx> PrirodaContext<'tcx> { } pub fn continue_execution(&mut self) -> InterpResult<'tcx> { - // TODO: stop when execution reaches a breakpoint. loop { self.step()?; + if self.is_at_breakpoint() { + return interp_ok(()); + } } } + fn set_breakpoint(&mut self, path: PathBuf, line: usize) { + self.breakpoints.insert(Breakpoint { path: normalize_path(path), line }); + } + + fn is_at_breakpoint(&self) -> bool { + // TODO: avoid repeated stops when one source line maps to multiple MIR statements. + let Some(location) = &self.current_location else { + return false; + }; + + let Some(path) = &location.local_path else { + return false; + }; + + self.breakpoints.contains(&Breakpoint { path: path.clone(), line: location.line }) + } + fn resolve_current_location(&self) -> Option { + // TODO: resolve macro-backed lines such as `println!` and `assert_eq!` for breakpoints. let span = self.ecx.machine.current_user_relevant_span(); if span.is_dummy() { return None; @@ -135,7 +167,8 @@ impl<'tcx> PrirodaContext<'tcx> { let loc = source_map.lookup_char_pos(span.lo()); Some(SourceLocation { - local_path: loc.file.name.clone().into_local_path(), + // TODO: cache normalized source paths; this runs after every MIR step. + local_path: loc.file.name.clone().into_local_path().map(normalize_path), display: source_map.span_to_diagnostic_string(span), line: loc.line, column: loc.col_display + 1, @@ -156,6 +189,11 @@ impl<'tcx> PrirodaContext<'tcx> { SessionCommand::Step => self.step(), SessionCommand::Quit => unreachable!("quit is handled by the CLI loop"), SessionCommand::Continue => self.continue_execution(), + SessionCommand::Breakpoint(path, line) => { + // TODO: print a breakpoint confirmation instead of treating this like an execution step. + self.set_breakpoint(path, line); + interp_ok(()) + } } } } @@ -164,13 +202,28 @@ enum SessionCommand { Step, Quit, Continue, + Breakpoint(PathBuf, usize), +} + +fn parse_breakpoint(input: &str) -> Option { + // TODO: reject empty paths and line 0 with a useful `usage: break :` error. + let (path, line) = input.rsplit_once(':')?; + let line = line.parse().ok()?; + + Some(SessionCommand::Breakpoint(PathBuf::from(path), line)) } fn parse_command(input: &str) -> Option { - match input.trim() { + let input = input.trim(); + let mut parts = input.splitn(2, char::is_whitespace); + let command = parts.next().unwrap_or(""); + let args = parts.next().unwrap_or("").trim(); + + match command { "" | "s" | "step" => Some(SessionCommand::Step), "q" | "quit" => Some(SessionCommand::Quit), "c" | "continue" => Some(SessionCommand::Continue), + "b" | "break" => parse_breakpoint(args), _ => None, } } From b0d748d2f88b045d383f76a896ed8e6e248705cd Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 31 May 2026 14:42:34 +0300 Subject: [PATCH 31/71] [Priroda] Skip hidden MIR instructions while stepping --- src/tools/miri/priroda/src/main.rs | 56 +++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 43418d0a03e4b..407997ff440bc 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -19,9 +19,11 @@ use miri::*; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; use rustc_interface::interface; +use rustc_middle::mir; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; + fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") .expect("set MIRI_SYSROOT to the path from `cargo miri setup --print-sysroot`") @@ -115,24 +117,42 @@ fn normalize_path(path: PathBuf) -> PathBuf { path.canonicalize().unwrap_or(path) } +enum InstructionVisibility { + NoInstruction, + Hidden, + Visible, +} + impl<'tcx> PrirodaContext<'tcx> { fn new(ecx: MiriInterpCx<'tcx>) -> Self { Self { ecx, breakpoints: HashSet::new(), current_location: None, last_location: None } } - // TODO: return a StepResult enum once we distinguish breakpoint stops, - // program exit, and other debugger states. - pub fn step(&mut self) -> InterpResult<'tcx> { + /// Advance Miri by one interpreter-loop transition. + fn advance(&mut self) -> InterpResult<'tcx> { // state inspection should happen only after a successful step self.ecx.miri_step()?; self.last_location = self.current_location.take(); self.current_location = self.resolve_current_location(); interp_ok(()) } + // TODO: return a StepResult enum once we distinguish breakpoint stops, + // program exit, and other debugger states. + /// Step to the next visible MIR instruction + pub fn step(&mut self) -> InterpResult<'tcx> { + loop { + self.advance()?; + + match self.current_instruction_visibility() { + InstructionVisibility::NoInstruction | InstructionVisibility::Hidden => {} + InstructionVisibility::Visible => return interp_ok(()), + } + } + } pub fn continue_execution(&mut self) -> InterpResult<'tcx> { loop { - self.step()?; + self.advance()?; if self.is_at_breakpoint() { return interp_ok(()); } @@ -143,6 +163,34 @@ impl<'tcx> PrirodaContext<'tcx> { self.breakpoints.insert(Breakpoint { path: normalize_path(path), line }); } + fn current_instruction_visibility(&self) -> InstructionVisibility { + // If the active thread has no stack frame, there is no MIR instruction to show. + let Some(frame) = self.ecx.active_thread_stack().last() else { + return InstructionVisibility::NoInstruction; + }; + + // `Right(span)` means the frame has source context but no precise MIR program-counter location. + let Either::Left(location) = frame.current_loc() else { + return InstructionVisibility::NoInstruction; + }; + + let basic_block = &frame.body().basic_blocks[location.block]; + + // `statement_index == statements.len()` points at the block terminator. + // Terminators affect control flow, so they are always visible. + let Some(statement) = basic_block.statements.get(location.statement_index) else { + return InstructionVisibility::Visible; + }; + + // Hide bookkeeping-only MIR statements during manual stepping. + match statement.kind { + mir::StatementKind::StorageLive(_) + | mir::StatementKind::StorageDead(_) + | mir::StatementKind::Nop => InstructionVisibility::Hidden, + _ => InstructionVisibility::Visible, + } + } + fn is_at_breakpoint(&self) -> bool { // TODO: avoid repeated stops when one source line maps to multiple MIR statements. let Some(location) = &self.current_location else { From b151668df9eb8d9c2ccf115180f4a7c56a58818a Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 31 May 2026 20:20:01 +0300 Subject: [PATCH 32/71] [Priroda] Centralize execution resume policy --- src/tools/miri/priroda/src/main.rs | 56 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 407997ff440bc..327c5b93f2d9a 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -106,6 +106,11 @@ struct Breakpoint { line: usize, } +enum ResumeMode { + MirInstruction, + Continue, +} + pub struct PrirodaContext<'tcx> { ecx: MiriInterpCx<'tcx>, breakpoints: HashSet, @@ -128,35 +133,50 @@ impl<'tcx> PrirodaContext<'tcx> { Self { ecx, breakpoints: HashSet::new(), current_location: None, last_location: None } } + /// Advance execution until the selected resume mode reaches a stopping point. + // TODO: return a StepResult enum once we distinguish breakpoint stops, + // program exit, and other debugger states. + fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx> { + loop { + self.advance()?; + + // An explicit breakpoint should stop execution even when the current + // MIR instruction would normally be hidden during manual stepping. + if self.is_at_breakpoint() { + return interp_ok(()); + } + + match mode { + ResumeMode::MirInstruction + if matches!( + self.current_instruction_visibility(), + InstructionVisibility::Visible + ) => + { + return interp_ok(()); + } + ResumeMode::MirInstruction | ResumeMode::Continue => {} + } + } + } + /// Advance Miri by one interpreter-loop transition. fn advance(&mut self) -> InterpResult<'tcx> { - // state inspection should happen only after a successful step + // State inspection should happen only after a successful step. self.ecx.miri_step()?; self.last_location = self.current_location.take(); self.current_location = self.resolve_current_location(); interp_ok(()) } - // TODO: return a StepResult enum once we distinguish breakpoint stops, - // program exit, and other debugger states. - /// Step to the next visible MIR instruction - pub fn step(&mut self) -> InterpResult<'tcx> { - loop { - self.advance()?; - match self.current_instruction_visibility() { - InstructionVisibility::NoInstruction | InstructionVisibility::Hidden => {} - InstructionVisibility::Visible => return interp_ok(()), - } - } + /// Step to the next visible MIR instruction. + pub fn step(&mut self) -> InterpResult<'tcx> { + self.resume(ResumeMode::MirInstruction) } + /// Continue execution until reaching a breakpoint or program termination. pub fn continue_execution(&mut self) -> InterpResult<'tcx> { - loop { - self.advance()?; - if self.is_at_breakpoint() { - return interp_ok(()); - } - } + self.resume(ResumeMode::Continue) } fn set_breakpoint(&mut self, path: PathBuf, line: usize) { From 395bd02c95d5b886ef212db2670377c4f84ee3b3 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Mon, 1 Jun 2026 15:55:10 +0300 Subject: [PATCH 33/71] [Priroda] Separate CLI handling from debugger state Move command parsing and presentation into a dedicated CLI frontend. Return structured command results from PrirodaContext so the frontend can distinguish normal steps, breakpoint stops, breakpoint additions, and session termination. --- src/tools/miri/priroda/src/main.rs | 249 +++++++++++++++++------------ 1 file changed, 149 insertions(+), 100 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 327c5b93f2d9a..b887ccf7d4fc6 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -42,13 +42,14 @@ fn main() { args.push(sysroot_flag); args.push(find_sysroot()); } - //TODO: handle the same `-Z` flags that Miri accepts. + // FIXME: handle the same `-Z` flags that Miri accepts. rustc_driver::run_compiler(&args, &mut PrirodaCompilerCalls::new()); } struct PrirodaCompilerCalls; impl PrirodaCompilerCalls { + // FIXME: remove this constructor if PrirodaCompilerCalls remains a unit struct. fn new() -> Self { Self } @@ -60,22 +61,24 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { tcx.dcx().abort_if_errors(); if !tcx.crate_types().contains(&CrateType::Executable) { - //TODO: support non-bin crates by listing functions and letting users call them with manually entered arguments. + // FIXME: support non-bin crates by listing functions and letting users call them with manually entered arguments. tcx.dcx().fatal("priroda only makes sense on bin crates"); } let ecx = create_ecx(tcx); let mut session = PrirodaContext::new(ecx); - let result = run_cli_loop(&mut session); + let cli = CLI {}; + let result = cli.run_cli_loop(&mut session); match result.report_err() { Ok(()) => {} Err(err) => if let Some((return_code, _leak_check)) = report_result(&session.ecx, err) { - // TODO: translate Miri termination into a Priroda execution-state enum so + // FIXME: translate Miri termination into a Priroda execution-state enum so // the CLI loop can distinguish whole-program exit from individual thread - // completion, print the exit code, and return to the debugger prompt. + // completion, run Miri-equivalent leak checks, print the exit code, and + // return to the debugger prompt. println!("program finished with exit code {return_code}"); if return_code != 0 { std::process::exit(return_code); @@ -89,61 +92,97 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { fn create_ecx<'tcx>(tcx: TyCtxt<'tcx>) -> MiriInterpCx<'tcx> { let (entry_id, entry_type) = miri::entry_fn(tcx); + // FIXME: share Miri launcher configuration so interpreted programs receive + // their program name, arguments, environment snapshot, and `MIRI_CWD`. let config = MiriConfig::default(); + // FIXME: report interpreter initialization failures instead of panicking. miri::create_ecx(tcx, entry_id, entry_type, &config, None).unwrap() } +/// Structured source information for frontends. struct SourceLocation { + // The path can be absent for synthetic spans. local_path: Option, display: String, line: usize, - column: usize, } -#[derive(Eq, Hash, PartialEq)] +/// A source-level breakpoint matched by path and line. +#[derive(Clone, Eq, Hash, PartialEq)] struct Breakpoint { path: PathBuf, line: usize, } -enum ResumeMode { - MirInstruction, - Continue, -} - -pub struct PrirodaContext<'tcx> { +/// Owns one interpreter session and its debugger state. +/// +/// Frontend rendering should eventually live outside this type. +struct PrirodaContext<'tcx> { ecx: MiriInterpCx<'tcx>, breakpoints: HashSet, current_location: Option, last_location: Option, } -fn normalize_path(path: PathBuf) -> PathBuf { - path.canonicalize().unwrap_or(path) +/// Controls when execution returns to the frontend. +enum ResumeMode { + /// Stop at the next visible MIR instruction. + MirInstruction, + /// Continue until reaching a breakpoint. + Continue, } +/// Describes whether the current MIR instruction should be shown to the user. enum InstructionVisibility { NoInstruction, Hidden, Visible, } +/// Describes why execution stopped and returned control to the frontend. +enum StepResult { + Step, + Breakpoint, +} + +fn normalize_path(path: PathBuf) -> PathBuf { + path.canonicalize().unwrap_or(path) +} + impl<'tcx> PrirodaContext<'tcx> { fn new(ecx: MiriInterpCx<'tcx>) -> Self { Self { ecx, breakpoints: HashSet::new(), current_location: None, last_location: None } } + /// Step to the next visible MIR instruction. + fn step(&mut self) -> InterpResult<'tcx, StepResult> { + self.resume(ResumeMode::MirInstruction) + } + + /// Continue execution until reaching a breakpoint or propagating termination. + fn continue_execution(&mut self) -> InterpResult<'tcx, StepResult> { + self.resume(ResumeMode::Continue) + } + + fn set_breakpoint(&mut self, path: PathBuf, line: usize) -> Breakpoint { + // FIXME: validate breakpoints here so every frontend gets the same behavior. + // Reject empty paths, missing files, directories, and line 0. Decide whether + // out-of-range lines should be rejected or kept as pending breakpoints. + // Report duplicate registrations separately. + let breakpoint = Breakpoint { path: normalize_path(path), line }; + self.breakpoints.insert(breakpoint.clone()); + breakpoint + } + /// Advance execution until the selected resume mode reaches a stopping point. - // TODO: return a StepResult enum once we distinguish breakpoint stops, - // program exit, and other debugger states. - fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx> { + fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx, StepResult> { loop { self.advance()?; // An explicit breakpoint should stop execution even when the current // MIR instruction would normally be hidden during manual stepping. if self.is_at_breakpoint() { - return interp_ok(()); + return interp_ok(StepResult::Breakpoint); } match mode { @@ -153,7 +192,7 @@ impl<'tcx> PrirodaContext<'tcx> { InstructionVisibility::Visible ) => { - return interp_ok(()); + return interp_ok(StepResult::Step); } ResumeMode::MirInstruction | ResumeMode::Continue => {} } @@ -162,6 +201,9 @@ impl<'tcx> PrirodaContext<'tcx> { /// Advance Miri by one interpreter-loop transition. fn advance(&mut self) -> InterpResult<'tcx> { + // FIXME: use a Miri-owned scheduler-aware debugger step API before + // claiming support for multi-threaded interpreted programs. + // State inspection should happen only after a successful step. self.ecx.miri_step()?; self.last_location = self.current_location.take(); @@ -169,20 +211,6 @@ impl<'tcx> PrirodaContext<'tcx> { interp_ok(()) } - /// Step to the next visible MIR instruction. - pub fn step(&mut self) -> InterpResult<'tcx> { - self.resume(ResumeMode::MirInstruction) - } - - /// Continue execution until reaching a breakpoint or program termination. - pub fn continue_execution(&mut self) -> InterpResult<'tcx> { - self.resume(ResumeMode::Continue) - } - - fn set_breakpoint(&mut self, path: PathBuf, line: usize) { - self.breakpoints.insert(Breakpoint { path: normalize_path(path), line }); - } - fn current_instruction_visibility(&self) -> InstructionVisibility { // If the active thread has no stack frame, there is no MIR instruction to show. let Some(frame) = self.ecx.active_thread_stack().last() else { @@ -212,7 +240,7 @@ impl<'tcx> PrirodaContext<'tcx> { } fn is_at_breakpoint(&self) -> bool { - // TODO: avoid repeated stops when one source line maps to multiple MIR statements. + // FIXME: avoid repeated stops when one source line maps to multiple MIR statements. let Some(location) = &self.current_location else { return false; }; @@ -225,7 +253,8 @@ impl<'tcx> PrirodaContext<'tcx> { } fn resolve_current_location(&self) -> Option { - // TODO: resolve macro-backed lines such as `println!` and `assert_eq!` for breakpoints. + // FIXME: resolve macro-backed lines such as `println!` and `assert_eq!` + // through `span.source_callsite()` before matching breakpoints. let span = self.ecx.machine.current_user_relevant_span(); if span.is_dummy() { return None; @@ -235,94 +264,114 @@ impl<'tcx> PrirodaContext<'tcx> { let loc = source_map.lookup_char_pos(span.lo()); Some(SourceLocation { - // TODO: cache normalized source paths; this runs after every MIR step. + // FIXME: cache normalized source paths; this runs after every MIR step. local_path: loc.file.name.clone().into_local_path().map(normalize_path), + // FIXME: keep presentation formatting in the CLI layer; the context + // should expose structured path and line data. display: source_map.span_to_diagnostic_string(span), line: loc.line, - column: loc.col_display + 1, }) } - pub fn print_location(&self) { - // TODO: skip noisy std/runtime spans and avoid printing `no-location` - // once the basic command loop is solid. - match &self.current_location { - Some(location) => println!("{}", location.display), - None => println!("no-location"), - } - io::stdout().flush().unwrap(); - } - fn run_command(&mut self, command: SessionCommand) -> InterpResult<'tcx> { + fn run_command(&mut self, command: DebuggerCommand) -> InterpResult<'tcx, CommandResult> { match command { - SessionCommand::Step => self.step(), - SessionCommand::Quit => unreachable!("quit is handled by the CLI loop"), - SessionCommand::Continue => self.continue_execution(), - SessionCommand::Breakpoint(path, line) => { - // TODO: print a breakpoint confirmation instead of treating this like an execution step. - self.set_breakpoint(path, line); - interp_ok(()) - } + DebuggerCommand::Step => self.step().map(CommandResult::ExecutionStopped), + DebuggerCommand::Continue => + self.continue_execution().map(CommandResult::ExecutionStopped), + DebuggerCommand::Breakpoint(path, line) => + interp_ok(CommandResult::BreakpointAdded(self.set_breakpoint(path, line))), + DebuggerCommand::TerminateSession => interp_ok(CommandResult::TerminateSession), } } } -enum SessionCommand { +enum DebuggerCommand { Step, - Quit, + TerminateSession, Continue, Breakpoint(PathBuf, usize), } -fn parse_breakpoint(input: &str) -> Option { - // TODO: reject empty paths and line 0 with a useful `usage: break :` error. - let (path, line) = input.rsplit_once(':')?; - let line = line.parse().ok()?; - - Some(SessionCommand::Breakpoint(PathBuf::from(path), line)) -} - -fn parse_command(input: &str) -> Option { - let input = input.trim(); - let mut parts = input.splitn(2, char::is_whitespace); - let command = parts.next().unwrap_or(""); - let args = parts.next().unwrap_or("").trim(); - - match command { - "" | "s" | "step" => Some(SessionCommand::Step), - "q" | "quit" => Some(SessionCommand::Quit), - "c" | "continue" => Some(SessionCommand::Continue), - "b" | "break" => parse_breakpoint(args), - _ => None, - } +enum CommandResult { + ExecutionStopped(StepResult), + BreakpointAdded(Breakpoint), + // FIXME: distinguish terminating the debugger session from disconnecting a + // frontend and terminating the interpreted program once multiple frontends exist. + TerminateSession, } -fn run_cli_loop<'tcx>(session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> { - loop { - print!("(priroda) "); - io::stdout().flush().unwrap(); +struct CLI; - let mut input = String::new(); - let bytes_read = io::stdin().read_line(&mut input).unwrap(); +impl CLI { + pub fn run_cli_loop<'tcx>(&self, session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> { + loop { + print!("(priroda) "); + io::stdout().flush().unwrap(); - if bytes_read == 0 { - println!("stdin closed, stopping"); - return interp_ok(()); - } + let mut input = String::new(); + let bytes_read = io::stdin().read_line(&mut input).unwrap(); - if let Some(command) = parse_command(&input) { - match command { - SessionCommand::Quit => { - println!("quitting"); - return interp_ok(()); - } + if bytes_read == 0 { + println!("stdin closed, stopping"); + return interp_ok(()); + } - command => { - session.run_command(command)?; - session.print_location(); + if let Some(command) = self.parse_command(&input) { + match session.run_command(command)? { + CommandResult::ExecutionStopped(result) => { + if matches!(result, StepResult::Breakpoint) { + println!("Hit breakpoint"); + } + self.print_location(&session); + } + CommandResult::BreakpointAdded(bp) => { + println!("breakpoint added: {}:{}", bp.path.display(), bp.line); + } + CommandResult::TerminateSession => { + println!("quitting"); + return interp_ok(()); + } } + } else { + println!("no command"); } - } else { - println!("no command"); } } + + fn parse_command(&self, input: &str) -> Option { + // FIXME: we need to distinguish malformed input from the unknown commands by returning useful + // command error that describes if it malformed or non exist command + let input = input.trim(); + let mut parts = input.splitn(2, char::is_whitespace); + let command = parts.next().unwrap_or(""); + let args = parts.next().unwrap_or("").trim(); + + match command { + "" | "s" | "step" => Some(DebuggerCommand::Step), + "q" | "quit" => Some(DebuggerCommand::TerminateSession), + "c" | "continue" => Some(DebuggerCommand::Continue), + "b" | "break" => self.parse_breakpoint(args), + _ => None, + } + } + + fn print_location(&self, session: &PrirodaContext) { + // FIXME: skip noisy std/runtime spans and avoid printing `no-location` + // once the basic command loop is solid. + match &session.current_location { + Some(location) => println!("{}", location.display), + None => println!("no-location"), + } + io::stdout().flush().unwrap(); + } + + fn parse_breakpoint(&self, input: &str) -> Option { + // FIXME: return a typed CommandError so malformed breakpoint input is + // distinguishable from an unknown command. Semantic validation belongs + // in PrirodaContext::set_breakpoint so non-CLI frontends cannot bypass it. + let (path, line) = input.rsplit_once(':')?; + let line = line.parse().ok()?; + + Some(DebuggerCommand::Breakpoint(PathBuf::from(path), line)) + } } From ffcfd584a1276348dee0016ad06cfe431a976081 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 5 Jun 2026 03:03:09 +0300 Subject: [PATCH 34/71] [Priroda] Index breakpoints by normalized path and line --- src/tools/miri/priroda/src/main.rs | 57 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index b887ccf7d4fc6..8277651a80393 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -11,7 +11,7 @@ extern crate rustc_log; extern crate rustc_middle; extern crate rustc_session; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::io::{self, Write}; use std::path::PathBuf; @@ -107,19 +107,15 @@ struct SourceLocation { line: usize, } -/// A source-level breakpoint matched by path and line. -#[derive(Clone, Eq, Hash, PartialEq)] -struct Breakpoint { - path: PathBuf, - line: usize, -} +/// Source-level breakpoints indexed by normalized path, then line. +type BreakpointTable = HashMap>; /// Owns one interpreter session and its debugger state. /// /// Frontend rendering should eventually live outside this type. struct PrirodaContext<'tcx> { ecx: MiriInterpCx<'tcx>, - breakpoints: HashSet, + breakpoints: BreakpointTable, current_location: Option, last_location: Option, } @@ -151,7 +147,7 @@ fn normalize_path(path: PathBuf) -> PathBuf { impl<'tcx> PrirodaContext<'tcx> { fn new(ecx: MiriInterpCx<'tcx>) -> Self { - Self { ecx, breakpoints: HashSet::new(), current_location: None, last_location: None } + Self { ecx, breakpoints: HashMap::new(), current_location: None, last_location: None } } /// Step to the next visible MIR instruction. @@ -164,14 +160,17 @@ impl<'tcx> PrirodaContext<'tcx> { self.resume(ResumeMode::Continue) } - fn set_breakpoint(&mut self, path: PathBuf, line: usize) -> Breakpoint { + fn set_breakpoint(&mut self, path: PathBuf, line: usize) -> BreakpointSetResult { // FIXME: validate breakpoints here so every frontend gets the same behavior. // Reject empty paths, missing files, directories, and line 0. Decide whether // out-of-range lines should be rejected or kept as pending breakpoints. // Report duplicate registrations separately. - let breakpoint = Breakpoint { path: normalize_path(path), line }; - self.breakpoints.insert(breakpoint.clone()); - breakpoint + + let path = normalize_path(path); + match self.breakpoints.entry(path.clone()).or_default().insert(line) { + true => BreakpointSetResult::Added(path, line), + false => BreakpointSetResult::Duplicate, + } } /// Advance execution until the selected resume mode reaches a stopping point. @@ -249,7 +248,11 @@ impl<'tcx> PrirodaContext<'tcx> { return false; }; - self.breakpoints.contains(&Breakpoint { path: path.clone(), line: location.line }) + let lines = match self.breakpoints.get(path) { + Some(lines) => lines, + None => return false, + }; + lines.contains(&location.line) } fn resolve_current_location(&self) -> Option { @@ -264,6 +267,8 @@ impl<'tcx> PrirodaContext<'tcx> { let loc = source_map.lookup_char_pos(span.lo()); Some(SourceLocation { + // TODO: store the span here instead and compute the normalized path + // lazily only when a caller actually needs it. // FIXME: cache normalized source paths; this runs after every MIR step. local_path: loc.file.name.clone().into_local_path().map(normalize_path), // FIXME: keep presentation formatting in the CLI layer; the context @@ -279,7 +284,7 @@ impl<'tcx> PrirodaContext<'tcx> { DebuggerCommand::Continue => self.continue_execution().map(CommandResult::ExecutionStopped), DebuggerCommand::Breakpoint(path, line) => - interp_ok(CommandResult::BreakpointAdded(self.set_breakpoint(path, line))), + interp_ok(CommandResult::BreakpointResult(self.set_breakpoint(path, line))), DebuggerCommand::TerminateSession => interp_ok(CommandResult::TerminateSession), } } @@ -292,9 +297,15 @@ enum DebuggerCommand { Breakpoint(PathBuf, usize), } +enum BreakpointSetResult { + Added(PathBuf, usize), + Duplicate, + // FIXME: add pending breakpoint support later if needed. +} + enum CommandResult { ExecutionStopped(StepResult), - BreakpointAdded(Breakpoint), + BreakpointResult(BreakpointSetResult), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -324,9 +335,13 @@ impl CLI { } self.print_location(&session); } - CommandResult::BreakpointAdded(bp) => { - println!("breakpoint added: {}:{}", bp.path.display(), bp.line); - } + CommandResult::BreakpointResult(res) => + match res { + BreakpointSetResult::Added(path, line) => + println!("breakpoint added: {}:{}", path.display(), line), + + BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), + }, CommandResult::TerminateSession => { println!("quitting"); return interp_ok(()); @@ -335,10 +350,14 @@ impl CLI { } else { println!("no command"); } + + io::stdout().flush().unwrap(); } } fn parse_command(&self, input: &str) -> Option { + // TODO: look at the Spanned crate for how to easily produce errors in + // rustc's style while manually parsing text input. // FIXME: we need to distinguish malformed input from the unknown commands by returning useful // command error that describes if it malformed or non exist command let input = input.trim(); From e5f0e6bcfc258344e336770d583ed4c11aa76e1e Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 5 Jun 2026 05:12:42 +0300 Subject: [PATCH 35/71] [Priroda] Store spans and compute normalized paths lazily --- src/tools/miri/priroda/src/main.rs | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 8277651a80393..6ac0ce761dcb0 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -10,6 +10,7 @@ extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; extern crate rustc_session; +extern crate rustc_span; use std::collections::{HashMap, HashSet}; use std::io::{self, Write}; @@ -23,6 +24,8 @@ use rustc_middle::mir; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; +use rustc_span::Span; +use rustc_span::source_map::SourceMap; fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") @@ -101,12 +104,18 @@ fn create_ecx<'tcx>(tcx: TyCtxt<'tcx>) -> MiriInterpCx<'tcx> { /// Structured source information for frontends. struct SourceLocation { - // The path can be absent for synthetic spans. - local_path: Option, - display: String, + // storing `span` to use it lazily to compute path. + span: Span, line: usize, } +impl SourceLocation { + fn local_path(&self, source_map: &SourceMap) -> Option { + let loc = source_map.lookup_char_pos(self.span.lo()); + loc.file.name.clone().into_local_path().map(normalize_path) + } +} + /// Source-level breakpoints indexed by normalized path, then line. type BreakpointTable = HashMap>; @@ -244,7 +253,8 @@ impl<'tcx> PrirodaContext<'tcx> { return false; }; - let Some(path) = &location.local_path else { + let source_map = self.ecx.tcx.sess.source_map(); + let Some(path) = &location.local_path(source_map) else { return false; }; @@ -266,16 +276,7 @@ impl<'tcx> PrirodaContext<'tcx> { let source_map = self.ecx.tcx.sess.source_map(); let loc = source_map.lookup_char_pos(span.lo()); - Some(SourceLocation { - // TODO: store the span here instead and compute the normalized path - // lazily only when a caller actually needs it. - // FIXME: cache normalized source paths; this runs after every MIR step. - local_path: loc.file.name.clone().into_local_path().map(normalize_path), - // FIXME: keep presentation formatting in the CLI layer; the context - // should expose structured path and line data. - display: source_map.span_to_diagnostic_string(span), - line: loc.line, - }) + Some(SourceLocation { span: span, line: loc.line }) } fn run_command(&mut self, command: DebuggerCommand) -> InterpResult<'tcx, CommandResult> { @@ -375,10 +376,14 @@ impl CLI { } fn print_location(&self, session: &PrirodaContext) { - // FIXME: skip noisy std/runtime spans and avoid printing `no-location` - // once the basic command loop is solid. + let source_map = session.ecx.tcx.sess.source_map(); match &session.current_location { - Some(location) => println!("{}", location.display), + Some(location) => + if let Some(path) = location.local_path(source_map) { + println!("{}:{}", path.display(), location.line); + } else { + println!("{}", source_map.span_to_diagnostic_string(location.span)); + }, None => println!("no-location"), } io::stdout().flush().unwrap(); From 04550539e975691f606a5e0170b3a02e392b8a8f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 7 Jun 2026 13:26:38 +0200 Subject: [PATCH 36/71] triagebot: remove assign.custom_welcome_messages --- src/tools/miri/triagebot.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 57c8f5782e0a5..727d3cc868742 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -17,12 +17,6 @@ allow-unauthenticated = [ [assign] warn_non_default_branch = true contributing_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#pr-review-process" -[assign.custom_welcome_messages] -welcome-message = "(unused)" -welcome-message-no-reviewer = """ -Thank you for contributing to Miri! A reviewer will take a look at your PR, typically within a week or two. -Please remember to not force-push to the PR branch except when you need to rebase due to a conflict or when the reviewer asks you for it. -""" [no-merges] exclude_titles = ["Rustup"] From cd6c3a698591d7ee262d25b042f74e1bf0f5e0d7 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 7 Jun 2026 00:58:25 +0300 Subject: [PATCH 37/71] [Priroda] Add Priroda CLI integration test --- src/tools/miri/priroda/README.md | 13 ++- src/tools/miri/priroda/tests/cli.rs | 85 +++++++++++++++++++ .../miri/priroda/tests/cli/empty_main.stdin | 1 + .../miri/priroda/tests/cli/empty_main.stdout | 1 + 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/tools/miri/priroda/tests/cli.rs create mode 100644 src/tools/miri/priroda/tests/cli/empty_main.stdin create mode 100644 src/tools/miri/priroda/tests/cli/empty_main.stdout diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 4077e07bcce67..6fe4ca23d59ed 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -28,10 +28,19 @@ export MIRI_SYSROOT="$(cargo +miri miri setup --print-sysroot)" ## Run -Priroda currently reads `MIRI_SYSROOT` directly. After setup: +Priroda currently reads `MIRI_SYSROOT` directly. After setup, run Priroda +from `miri/priroda/`: ```sh -cargo run -p priroda -- tests/pass/empty_main.rs +cargo run -- ../tests/pass/empty_main.rs +``` + +## Test + +Priroda's CLI tests also need `MIRI_SYSROOT`. Run them from `miri/priroda/`: + +```sh +cargo test ``` ## Commands diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs new file mode 100644 index 0000000000000..4bec4de9b6776 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli.rs @@ -0,0 +1,85 @@ +use std::io::Write; +use std::path::PathBuf; +use std::process::{Command, Stdio}; + +/// Runs one scripted Priroda CLI fixture against a Rust program. +/// +/// `test_path` is the extensionless fixture stem. The helper sends +/// `.stdin` to Priroda and expects exact stdout from +/// `.stdout`. +fn run_cli_test(program_path: &str, test_path: &str) -> Result<(), Box> { + // Anchor relative paths to the crate root so the test does not depend on + // the current working directory used by a test runner or IDE. + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + let test_path = manifest_dir.join(test_path); + let stdin_path = test_path.with_extension("stdin"); + let stdout_path = test_path.with_extension("stdout"); + + // Keep scripted stdin as raw bytes: the helper only forwards it to the + // child process, so there is no need to validate it as UTF-8 here. + let input = std::fs::read(stdin_path)?; + + // Source paths printed for `std` frames come from the active rustc + // toolchain, not from `MIRI_SYSROOT`, so expand `{RUSTC_SYSROOT}` from + // `rustc --print sysroot`. Use strict UTF-8 because this value is part of + // the exact stdout contract; invalid output should fail the test. + let rustc_sysroot = Command::new("rustc").arg("--print").arg("sysroot").output()?; + let rustc_sysroot = String::from_utf8(rustc_sysroot.stdout)?.trim().to_owned(); + + // Keep fixture stdout exact while allowing machine-specific absolute paths + // to be written with stable placeholders. + let expected_output = std::fs::read_to_string(stdout_path)? + .replace("{MANIFEST_DIR}", &manifest_dir.display().to_string()) + .replace("{MIRI_DIR}", &manifest_dir.parent().unwrap().display().to_string()) + .replace("{RUSTC_SYSROOT}", &rustc_sysroot); + + let mut priroda = Command::new(env!("CARGO_BIN_EXE_priroda")) + .arg(manifest_dir.join(program_path)) + // The CLI contract checks stderr exactly, so inherited logging + // configuration would make the test fail for reasons unrelated to + // Priroda's behavior. + .env_remove("RUSTC_LOG") + .env_remove("RUST_LOG") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + priroda.stdin.as_mut().unwrap().write_all(&input)?; + // Close stdin after the scripted input so Priroda can observe EOF if a + // fixture does not explicitly quit. + drop(priroda.stdin.take()); + + let output = priroda.wait_with_output()?; + + assert!( + output.status.success(), + "priroda exited with status {}\nstderr:\n{}", + output.status, + // This is only diagnostic text for a failed assertion, so lossy UTF-8 is + // better than hiding stderr behind a second conversion failure. + String::from_utf8_lossy(&output.stderr), + ); + + assert!( + output.stderr.is_empty(), + "expected no stderr output, got:\n{}", + // Same reasoning as above: stderr is not part of the success path here; + // it is shown to make a failure easier to debug. + String::from_utf8_lossy(&output.stderr), + ); + + // Actual stdout is the value under test, so require valid UTF-8 before doing + // the exact string comparison against the expanded fixture. + assert_eq!(String::from_utf8(output.stdout)?, expected_output); + + Ok(()) +} + +/// Verifies Priroda can start on the simplest passing Rust program and accept +/// a scripted `quit` command. +#[test] +fn empty_main() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/empty_main") +} diff --git a/src/tools/miri/priroda/tests/cli/empty_main.stdin b/src/tools/miri/priroda/tests/cli/empty_main.stdin new file mode 100644 index 0000000000000..ff604669be5ca --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/empty_main.stdin @@ -0,0 +1 @@ +quit diff --git a/src/tools/miri/priroda/tests/cli/empty_main.stdout b/src/tools/miri/priroda/tests/cli/empty_main.stdout new file mode 100644 index 0000000000000..daad97b3037c2 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/empty_main.stdout @@ -0,0 +1 @@ +(priroda) quitting From 775c84d56c928bcfb3370e7826fd716f965c7f97 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 7 Jun 2026 03:05:16 +0300 Subject: [PATCH 38/71] [Priroda] Add CLI command handling tests --- src/tools/miri/priroda/tests/cli.rs | 20 +++++++++++++++++++ .../tests/cli/duplicate_breakpoint.stdin | 3 +++ .../tests/cli/duplicate_breakpoint.stdout | 3 +++ .../priroda/tests/cli/eof_exits_cleanly.stdin | 0 .../tests/cli/eof_exits_cleanly.stdout | 1 + .../priroda/tests/cli/invalid_commands.stdin | 4 ++++ .../priroda/tests/cli/invalid_commands.stdout | 4 ++++ 7 files changed, 35 insertions(+) create mode 100644 src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin create mode 100644 src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout create mode 100644 src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdin create mode 100644 src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout create mode 100644 src/tools/miri/priroda/tests/cli/invalid_commands.stdin create mode 100644 src/tools/miri/priroda/tests/cli/invalid_commands.stdout diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index 4bec4de9b6776..101c8cfe0cabf 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -83,3 +83,23 @@ fn run_cli_test(program_path: &str, test_path: &str) -> Result<(), Box Result<(), Box> { run_cli_test("../tests/pass/empty_main.rs", "tests/cli/empty_main") } + +/// Verifies EOF exits the debugger loop cleanly without requiring an explicit +/// quit command. +#[test] +fn eof_exits_cleanly() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/eof_exits_cleanly") +} + +/// Verifies unknown commands and malformed breakpoints are rejected without +/// mutating debugger state. +#[test] +fn invalid_commands() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/invalid_commands") +} + +/// Verifies breakpoint aliases and duplicate detection before execution starts. +#[test] +fn duplicate_breakpoint() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/duplicate_breakpoint") +} diff --git a/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin b/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin new file mode 100644 index 0000000000000..bf4065c08aa76 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin @@ -0,0 +1,3 @@ +break nowhere.rs:12 +b nowhere.rs:12 +quit diff --git a/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout b/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout new file mode 100644 index 0000000000000..c3ba71bcaf6d7 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout @@ -0,0 +1,3 @@ +(priroda) breakpoint added: nowhere.rs:12 +(priroda) Duplicate breakpoint +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdin b/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdin new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout b/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout new file mode 100644 index 0000000000000..ee4ed45becbee --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout @@ -0,0 +1 @@ +(priroda) stdin closed, stopping diff --git a/src/tools/miri/priroda/tests/cli/invalid_commands.stdin b/src/tools/miri/priroda/tests/cli/invalid_commands.stdin new file mode 100644 index 0000000000000..87d47a341b59c --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/invalid_commands.stdin @@ -0,0 +1,4 @@ +wat +break +break nowhere.rs:not-a-line +quit diff --git a/src/tools/miri/priroda/tests/cli/invalid_commands.stdout b/src/tools/miri/priroda/tests/cli/invalid_commands.stdout new file mode 100644 index 0000000000000..c48a6c72fde76 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/invalid_commands.stdout @@ -0,0 +1,4 @@ +(priroda) no command +(priroda) no command +(priroda) no command +(priroda) quitting From 764fe8c984fb3597d20bb9e9bdc2ddefa023c10c Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 7 Jun 2026 03:25:43 +0300 Subject: [PATCH 39/71] [Priroda] Add CLI continue execution tests --- src/tools/miri/priroda/tests/cli.rs | 13 +++++++++++++ .../tests/cli/continue_finishes_program.stdin | 1 + .../tests/cli/continue_finishes_program.stdout | 1 + .../tests/cli/continue_hits_breakpoint.stdin | 3 +++ .../tests/cli/continue_hits_breakpoint.stdout | 4 ++++ 5 files changed, 22 insertions(+) create mode 100644 src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin create mode 100644 src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout create mode 100644 src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin create mode 100644 src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index 101c8cfe0cabf..db2269e34dc73 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -103,3 +103,16 @@ fn invalid_commands() -> Result<(), Box> { fn duplicate_breakpoint() -> Result<(), Box> { run_cli_test("../tests/pass/empty_main.rs", "tests/cli/duplicate_breakpoint") } + +/// Verifies continue can drive the interpreted program to normal completion. +#[test] +fn continue_finishes_program() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/continue_finishes_program") +} + +/// Verifies continue stops when execution reaches a registered source-location +/// breakpoint. +#[test] +fn continue_hits_breakpoint() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/continue_hits_breakpoint") +} diff --git a/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin b/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin new file mode 100644 index 0000000000000..44c5d7d65ead7 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin @@ -0,0 +1 @@ +continue diff --git a/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout b/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout new file mode 100644 index 0000000000000..d6c4605d6baf3 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout @@ -0,0 +1 @@ +(priroda) program finished with exit code 0 diff --git a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin new file mode 100644 index 0000000000000..812cbe8eb2188 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin @@ -0,0 +1,3 @@ +break ../tests/pass/empty_main.rs:3 +continue +quit diff --git a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout new file mode 100644 index 0000000000000..50a0905ec56cb --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout @@ -0,0 +1,4 @@ +(priroda) breakpoint added: {MIRI_DIR}/tests/pass/empty_main.rs:3 +(priroda) Hit breakpoint +{MIRI_DIR}/tests/pass/empty_main.rs:3 +(priroda) quitting From c91119ebbea17ca0c10b7098f56367ea75e1f686 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 7 Jun 2026 03:26:15 +0300 Subject: [PATCH 40/71] [Priroda] Add CLI stepping regression tests --- src/tools/miri/priroda/tests/cli.rs | 14 ++++++++++++++ .../tests/cli/repeated_same_line_breakpoint.stdin | 4 ++++ .../tests/cli/repeated_same_line_breakpoint.stdout | 6 ++++++ .../miri/priroda/tests/cli/step_aliases.stdin | 4 ++++ .../miri/priroda/tests/cli/step_aliases.stdout | 4 ++++ 5 files changed, 32 insertions(+) create mode 100644 src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin create mode 100644 src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout create mode 100644 src/tools/miri/priroda/tests/cli/step_aliases.stdin create mode 100644 src/tools/miri/priroda/tests/cli/step_aliases.stdout diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index db2269e34dc73..77d76c3e1d7e9 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -116,3 +116,17 @@ fn continue_finishes_program() -> Result<(), Box> { fn continue_hits_breakpoint() -> Result<(), Box> { run_cli_test("../tests/pass/empty_main.rs", "tests/cli/continue_hits_breakpoint") } + +/// Verifies every current spelling of MIR-instruction stepping advances +/// execution and reports a location. +#[test] +fn step_aliases() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/step_aliases") +} + +/// Documents the current repeated-stop behavior when multiple MIR locations map +/// to the same source breakpoint line. +#[test] +fn repeated_same_line_breakpoint() -> Result<(), Box> { + run_cli_test("../tests/pass/empty_main.rs", "tests/cli/repeated_same_line_breakpoint") +} diff --git a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin new file mode 100644 index 0000000000000..ac38e96931ba1 --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin @@ -0,0 +1,4 @@ +break ../tests/pass/empty_main.rs:3 +continue +continue +quit diff --git a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout new file mode 100644 index 0000000000000..106b53598992d --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout @@ -0,0 +1,6 @@ +(priroda) breakpoint added: {MIRI_DIR}/tests/pass/empty_main.rs:3 +(priroda) Hit breakpoint +{MIRI_DIR}/tests/pass/empty_main.rs:3 +(priroda) Hit breakpoint +{MIRI_DIR}/tests/pass/empty_main.rs:3 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/cli/step_aliases.stdin b/src/tools/miri/priroda/tests/cli/step_aliases.stdin new file mode 100644 index 0000000000000..923b000e78e7e --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/step_aliases.stdin @@ -0,0 +1,4 @@ +s +step + +quit diff --git a/src/tools/miri/priroda/tests/cli/step_aliases.stdout b/src/tools/miri/priroda/tests/cli/step_aliases.stdout new file mode 100644 index 0000000000000..32233f657b41a --- /dev/null +++ b/src/tools/miri/priroda/tests/cli/step_aliases.stdout @@ -0,0 +1,4 @@ +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) quitting From 8ed3e54dabc4fbe798343eebd35689f39140a374 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 7 Jun 2026 19:49:54 +0300 Subject: [PATCH 41/71] [Priroda] Add testing to CI job --- src/tools/miri/.github/workflows/ci.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 294c8dd3d5aa1..f929aaaa04b14 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -157,15 +157,22 @@ jobs: # This job is intentionally separate from `test` so that Priroda can be # developed as a separate crate inside the Miri repository for now. - priroda-build: + priroda: name: Priroda runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/workflows/setup + - name: install Miri driver + run: ./miri install - name: build Priroda working-directory: priroda run: cargo build --locked + - name: test Priroda + working-directory: priroda + run: | + sysroot="$(cargo miri setup --print-sysroot)" + MIRI_SYSROOT="$sysroot" cargo test --locked coverage: name: coverage report @@ -180,7 +187,7 @@ jobs: # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! # And they should be added below in `cron-fail-notify` as well. conclusion: - needs: [test, style, bootstrap, coverage, priroda-build] + needs: [test, style, bootstrap, coverage, priroda] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run @@ -264,7 +271,7 @@ jobs: cron-fail-notify: name: cronjob failure notification runs-on: ubuntu-latest - needs: [test, style, bootstrap, coverage, priroda-build] + needs: [test, style, bootstrap, coverage, priroda] if: ${{ github.event_name == 'schedule' && failure() }} steps: # Send a Zulip notification From b01a70d227dcc1c2236aeec7b656806cd0c021db Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 8 Jun 2026 05:56:39 +0000 Subject: [PATCH 42/71] Prepare for merging from rust-lang/rust This updates the rust-version file to 029c9e18dd1f4668e1d42bb187c1c263dfe20093. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 61262d3c6d418..387bd8edd2196 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3179a47d67719a92b4d3c3689aca391c40ff1a04 +029c9e18dd1f4668e1d42bb187c1c263dfe20093 From e789c33edbbd734fbda406691f4088726f826874 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 8 Jun 2026 21:41:50 +0200 Subject: [PATCH 43/71] Add missing tests and adress concerns --- .../coerce-shared-alias-projection.rs | 110 ++++++++++++++++++ .../coerce-shared-different-layout.rs | 41 +++++++ ...coerce-shared-marker-no-target-lifetime.rs | 21 ++++ .../reborrow/coerce-shared-multi-field.rs | 58 +++++++++ .../crashes/reborrow/coerce-shared-nested.rs | 62 ++++++++++ .../coerce-shared-tuple-phantom-position.rs | 86 ++++++++++++++ ...eld-mismatch-coerce-shared-issue-156315.rs | 22 ++++ ...ormed-marker-coerce-shared-issue-156309.rs | 30 +++++ ...generic-args-coerce-shared-issue-156315.rs | 24 ++++ .../auxiliary/reborrow_foreign_private.rs | 6 + .../coerce-shared-associated-type-field.rs | 30 +++++ ...coerce-shared-associated-type-field.stderr | 8 ++ .../coerce-shared-decl-macro-hygiene.rs | 26 +++++ .../coerce-shared-decl-macro-hygiene.stderr | 13 +++ .../ui/reborrow/coerce-shared-extra-marker.rs | 37 ++++++ .../coerce-shared-field-lifetime-swap.rs | 21 ++++ .../coerce-shared-field-lifetime-swap.stderr | 8 ++ .../reborrow/coerce-shared-field-relations.rs | 53 +++++++++ .../coerce-shared-field-relations.stderr | 9 ++ .../coerce-shared-foreign-private-field.rs | 20 ++++ ...erce-shared-foreign-private-tuple-field.rs | 22 ++++ tests/ui/reborrow/coerce-shared-generics.rs | 41 +++++++ .../ui/reborrow/coerce-shared-generics.stderr | 8 ++ .../coerce-shared-lifetime-mismatch.rs | 23 ++++ .../coerce-shared-lifetime-mismatch.stderr | 17 +++ .../coerce-shared-missing-target-field.rs | 22 ++++ .../coerce-shared-missing-target-field.stderr | 7 ++ .../coerce-shared-mut-ref-field-validation.rs | 45 +++++++ ...rce-shared-mut-ref-field-validation.stderr | 9 ++ ...hared-omitted-reborrow-field-after-dead.rs | 51 ++++++++ ...d-omitted-reborrow-field-after-dead.stderr | 8 ++ ...ce-shared-omitted-reborrow-field-locked.rs | 53 +++++++++ ...hared-omitted-reborrow-field-locked.stderr | 21 ++++ .../coerce-shared-omitted-reborrow-field.rs | 45 +++++++ ...oerce-shared-omitted-reborrow-field.stderr | 8 ++ .../reborrow/coerce-shared-reordered-field.rs | 32 +++++ .../coerce-shared-reordered-field.stderr | 8 ++ .../reborrow/coerce-shared-wrong-generic.rs | 23 ++++ .../coerce-shared-wrong-generic.stderr | 9 ++ 39 files changed, 1137 insertions(+) create mode 100644 tests/crashes/reborrow/coerce-shared-alias-projection.rs create mode 100644 tests/crashes/reborrow/coerce-shared-different-layout.rs create mode 100644 tests/crashes/reborrow/coerce-shared-marker-no-target-lifetime.rs create mode 100644 tests/crashes/reborrow/coerce-shared-multi-field.rs create mode 100644 tests/crashes/reborrow/coerce-shared-nested.rs create mode 100644 tests/crashes/reborrow/coerce-shared-tuple-phantom-position.rs create mode 100644 tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs create mode 100644 tests/crashes/reborrow/malformed-marker-coerce-shared-issue-156309.rs create mode 100644 tests/crashes/reborrow/missing-generic-args-coerce-shared-issue-156315.rs create mode 100644 tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs create mode 100644 tests/ui/reborrow/coerce-shared-associated-type-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-associated-type-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs create mode 100644 tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr create mode 100644 tests/ui/reborrow/coerce-shared-extra-marker.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr create mode 100644 tests/ui/reborrow/coerce-shared-field-relations.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-relations.stderr create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-generics.rs create mode 100644 tests/ui/reborrow/coerce-shared-generics.stderr create mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs create mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr create mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs create mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-reordered-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-reordered-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.rs create mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.stderr diff --git a/tests/crashes/reborrow/coerce-shared-alias-projection.rs b/tests/crashes/reborrow/coerce-shared-alias-projection.rs new file mode 100644 index 0000000000000..8497455de921a --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-alias-projection.rs @@ -0,0 +1,110 @@ +//@ known-bug: unknown +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +// This test combines alias/projection normalization with the leaf `&mut T` to `&T` +// shared reborrow path. + +struct InnerMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for InnerMut<'a, T> {} + +struct InnerRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for InnerRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for InnerRef<'a, T> {} + +impl<'a, T> CoerceShared> for InnerMut<'a, T> {} + +type DirectInnerRef<'a, T> = InnerRef<'a, T>; + +trait RefFamily<'a, T> { + type Ref; +} + +struct Projected; + +impl<'a, T: 'a> RefFamily<'a, T> for Projected { + type Ref = InnerRef<'a, T>; +} + +type ProjectedInnerRef<'a, T> = >::Ref; + +struct OuterMut<'a, T> { + inner: InnerMut<'a, T>, + tag: usize, +} + +impl<'a, T> Reborrow for OuterMut<'a, T> {} + +struct OuterAliasRef<'a, T> { + inner: DirectInnerRef<'a, T>, + tag: usize, +} + +impl<'a, T> Clone for OuterAliasRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for OuterAliasRef<'a, T> {} + +impl<'a, T> CoerceShared> for OuterMut<'a, T> {} + +struct OuterProjectionRef<'a, T: 'a> { + inner: ProjectedInnerRef<'a, T>, + tag: usize, +} + +impl<'a, T: 'a> Clone for OuterProjectionRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T: 'a> Copy for OuterProjectionRef<'a, T> {} + +impl<'a, T: 'a> CoerceShared> for OuterMut<'a, T> {} + +fn read_alias<'a>(outer: OuterAliasRef<'a, u32>) -> (&'a u32, usize) { + (outer.inner.value, outer.tag) +} + +fn read_projection<'a>(outer: OuterProjectionRef<'a, u32>) -> (&'a u32, usize) { + (outer.inner.value, outer.tag) +} + +const fn const_accept_projection(_outer: OuterProjectionRef<'_, u32>) {} + +const fn consteval_projection_reborrow() { + let mut value = 11; + const_accept_projection(OuterMut { + inner: InnerMut { value: &mut value }, + tag: 5, + }); +} + +fn main() { + const { consteval_projection_reborrow(); } + + let mut value = 22; + let outer = OuterMut { inner: InnerMut { value: &mut value }, tag: 7 }; + + let (alias_value, alias_tag) = read_alias(outer); + assert_eq!((*alias_value, alias_tag), (22, 7)); + + let (projection_value, projection_tag) = read_projection(outer); + assert_eq!((*projection_value, projection_tag), (22, 7)); +} diff --git a/tests/crashes/reborrow/coerce-shared-different-layout.rs b/tests/crashes/reborrow/coerce-shared-different-layout.rs new file mode 100644 index 0000000000000..ad285bf0130d1 --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-different-layout.rs @@ -0,0 +1,41 @@ +//@ known-bug: unknown +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; +use std::ptr::NonNull; + +struct ImbrisMut<'a, T> { + ptr: NonNull, + metadata: usize, + marker: PhantomData<&'a mut T>, +} + +impl<'a, T> Reborrow for ImbrisMut<'a, T> {} + +struct ImbrisRef<'a, T> { + ptr: NonNull, + marker: PhantomData<&'a T>, +} + +impl<'a, T> Clone for ImbrisRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for ImbrisRef<'a, T> {} + +impl<'a, T> CoerceShared> for ImbrisMut<'a, T> {} + +fn ptr(value: ImbrisRef<'_, i32>) -> NonNull { + value.ptr +} + +fn main() { + let mut value = 1; + let raw = NonNull::from(&mut value); + let wrapped = ImbrisMut { ptr: raw, metadata: 32, marker: PhantomData }; + + assert_eq!(ptr(wrapped), raw); +} diff --git a/tests/crashes/reborrow/coerce-shared-marker-no-target-lifetime.rs b/tests/crashes/reborrow/coerce-shared-marker-no-target-lifetime.rs new file mode 100644 index 0000000000000..3cdbf515371f2 --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-marker-no-target-lifetime.rs @@ -0,0 +1,21 @@ +//@ known-bug: unknown +//@ edition: 2024 + +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +struct CustomMarkerRef; + +impl<'a> Reborrow for CustomMarker<'a> {} +impl<'a> CoerceShared for CustomMarker<'a> {} +//~^ ERROR + +fn method(_a: CustomMarkerRef) {} + +fn main() { + let a = CustomMarker(PhantomData); + method(a); + //~^ ERROR +} diff --git a/tests/crashes/reborrow/coerce-shared-multi-field.rs b/tests/crashes/reborrow/coerce-shared-multi-field.rs new file mode 100644 index 0000000000000..3bf5b64064d4a --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-multi-field.rs @@ -0,0 +1,58 @@ +//@ known-bug: unknown +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; +use std::ptr::NonNull; + +struct MatMut<'a, T> { + ptr: NonNull, + rows: usize, + cols: usize, + row_stride: usize, + col_stride: usize, + marker: PhantomData<&'a mut T>, +} + +impl<'a, T> Reborrow for MatMut<'a, T> {} + +struct MatRef<'a, T> { + ptr: NonNull, + rows: usize, + cols: usize, + row_stride: usize, + col_stride: usize, + marker: PhantomData<&'a T>, +} + +impl<'a, T> Clone for MatRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for MatRef<'a, T> {} + +impl<'a, T> CoerceShared> for MatMut<'a, T> {} + +fn dims(mat: MatRef<'_, T>) -> (usize, usize, usize, usize) { + let _ = mat.ptr; + (mat.rows, mat.cols, mat.row_stride, mat.col_stride) +} + +fn main() { + let mut value = 0; + let mat = MatMut { + ptr: NonNull::from(&mut value), + rows: 2, + cols: 3, + row_stride: 4, + col_stride: 5, + marker: PhantomData, + }; + + assert_eq!(dims(mat), (2, 3, 4, 5)); + // Reusing the same source proves repeated shared reborrows keep source-only data protected + // without consuming the reborrowable value. + assert_eq!(dims(mat), (2, 3, 4, 5)); +} diff --git a/tests/crashes/reborrow/coerce-shared-nested.rs b/tests/crashes/reborrow/coerce-shared-nested.rs new file mode 100644 index 0000000000000..a916dde881ec5 --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-nested.rs @@ -0,0 +1,62 @@ +//@ known-bug: unknown +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct InnerMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for InnerMut<'a, T> {} + +struct InnerRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for InnerRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for InnerRef<'a, T> {} + +impl<'a, T> CoerceShared> for InnerMut<'a, T> {} + +struct OuterMut<'a, T> { + inner: InnerMut<'a, T>, + tag: usize, +} + +impl<'a, T> Reborrow for OuterMut<'a, T> {} + +struct OuterRef<'a, T> { + inner: InnerRef<'a, T>, + tag: usize, +} + +impl<'a, T> Clone for OuterRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for OuterRef<'a, T> {} + +impl<'a, T> CoerceShared> for OuterMut<'a, T> {} + +fn get<'a>(outer: OuterRef<'a, i32>) -> (&'a i32, usize) { + (outer.inner.value, outer.tag) +} + +fn main() { + let mut value = 22; + let outer = OuterMut { inner: InnerMut { value: &mut value }, tag: 7 }; + + let (first, tag) = get(outer); + assert_eq!((*first, tag), (22, 7)); + + let (second, tag) = get(outer); + assert_eq!((*second, tag), (22, 7)); +} diff --git a/tests/crashes/reborrow/coerce-shared-tuple-phantom-position.rs b/tests/crashes/reborrow/coerce-shared-tuple-phantom-position.rs new file mode 100644 index 0000000000000..0ada68c9c20f7 --- /dev/null +++ b/tests/crashes/reborrow/coerce-shared-tuple-phantom-position.rs @@ -0,0 +1,86 @@ +//@ known-bug: unknown +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct SourceLeadingMut<'a, T>(PhantomData<&'a mut T>, &'a mut T); + +impl<'a, T> Reborrow for SourceLeadingMut<'a, T> {} + +struct SourceLeadingRef<'a, T>(&'a T); + +impl<'a, T> Clone for SourceLeadingRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for SourceLeadingRef<'a, T> {} + +impl<'a, T> CoerceShared> for SourceLeadingMut<'a, T> {} + +struct TargetLeadingMut<'a, T>(&'a mut T); + +impl<'a, T> Reborrow for TargetLeadingMut<'a, T> {} + +struct TargetLeadingRef<'a, T>(PhantomData<&'a T>, &'a T); + +impl<'a, T> Clone for TargetLeadingRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for TargetLeadingRef<'a, T> {} + +impl<'a, T> CoerceShared> for TargetLeadingMut<'a, T> {} + +struct InterleavedMut<'a, T, U>( + PhantomData<&'a mut T>, + &'a mut T, + PhantomData<&'a mut U>, + &'a mut U, +); + +impl<'a, T, U> Reborrow for InterleavedMut<'a, T, U> {} + +struct InterleavedRef<'a, T, U>(&'a T, PhantomData<&'a T>, &'a U, PhantomData<&'a U>); + +impl<'a, T, U> Clone for InterleavedRef<'a, T, U> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T, U> Copy for InterleavedRef<'a, T, U> {} + +impl<'a, T, U> CoerceShared> for InterleavedMut<'a, T, U> {} + +fn read_source_leading<'a>(value: SourceLeadingRef<'a, i32>) -> &'a i32 { + value.0 +} + +fn read_target_leading<'a>(value: TargetLeadingRef<'a, i32>) -> &'a i32 { + value.1 +} + +fn read_interleaved<'a>(value: InterleavedRef<'a, i32, i64>) -> (&'a i32, &'a i64) { + (value.0, value.2) +} + +fn main() { + let mut source_leading = 10; + let wrapped = SourceLeadingMut(PhantomData, &mut source_leading); + assert_eq!(*read_source_leading(wrapped), 10); + + let mut target_leading = 20; + let wrapped = TargetLeadingMut(&mut target_leading); + assert_eq!(*read_target_leading(wrapped), 20); + + let mut first = 30; + let mut second = 40_i64; + let wrapped = InterleavedMut(PhantomData, &mut first, PhantomData, &mut second); + let (first, second) = read_interleaved(wrapped); + assert_eq!((*first, *second), (30, 40)); +} diff --git a/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs b/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs new file mode 100644 index 0000000000000..b9721d5244311 --- /dev/null +++ b/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs @@ -0,0 +1,22 @@ +//@ known-bug: unknown +//@ normalize-stderr: "\n\n\z" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct CustomMut<'a, T>(&'a mut T); + +impl<'a, T> Reborrow for CustomMut<'a, T> {} + +struct CustomRef<'a, T>(&'a CustomMut<'a, T>); +//~^ ERROR + +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +fn method(_a: CustomRef<'_, ()>) {} + +fn main() { + let a = CustomMut(&mut ()); + method(a); +} diff --git a/tests/crashes/reborrow/malformed-marker-coerce-shared-issue-156309.rs b/tests/crashes/reborrow/malformed-marker-coerce-shared-issue-156309.rs new file mode 100644 index 0000000000000..14b7ec820a180 --- /dev/null +++ b/tests/crashes/reborrow/malformed-marker-coerce-shared-issue-156309.rs @@ -0,0 +1,30 @@ +//@ known-bug: unknown +//@ edition: 2024 + +#![feature(reborrow)] + +// Malformed no-ICE regression: the unrelated name and signature errors are intentional because the +// original issue combined them with CoerceShared validation. + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); + +struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>); +//~^ ERROR +//~| ERROR +//~| ERROR + +impl<'a> Reborrow for CustomMarker<'a> {} +impl<'a> CoerceShared> for CustomMarker<'a> {} +//~^ ERROR + +fn method<'a>(_a: CustomMarkerRef<'a>) -> 'a () { + //~^ ERROR + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); +} diff --git a/tests/crashes/reborrow/missing-generic-args-coerce-shared-issue-156315.rs b/tests/crashes/reborrow/missing-generic-args-coerce-shared-issue-156315.rs new file mode 100644 index 0000000000000..d365986901fe3 --- /dev/null +++ b/tests/crashes/reborrow/missing-generic-args-coerce-shared-issue-156315.rs @@ -0,0 +1,24 @@ +//@ known-bug: unknown +#![feature(reborrow)] + +// Malformed no-ICE regression: this intentionally keeps the missing generic arguments from the +// original reproducer while the corrected test isolates the CoerceShared field error. + +use std::marker::{CoerceShared, Reborrow}; + +struct CustomMut<'a, T>(&'a mut T); + +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +struct CustomRef<'a, T>(&'a CustomMut); +//~^ ERROR +//~| ERROR +//~| ERROR + +fn method(_a: CustomRef<'_, ()>) {} + +fn main() { + let a = CustomMut(&mut ()); + method(a); +} diff --git a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs new file mode 100644 index 0000000000000..d9a1029211136 --- /dev/null +++ b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs @@ -0,0 +1,6 @@ +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct ForeignRef<'a> { + value: &'a i32, +} diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.rs b/tests/ui/reborrow/coerce-shared-associated-type-field.rs new file mode 100644 index 0000000000000..df744e9442dd8 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.rs @@ -0,0 +1,30 @@ +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +trait Trait { + type Assoc; +} + +impl Trait for i32 { + type Assoc = i64; +} + +struct MyMut<'a> { + x: &'a (), + y: i64, +} + +#[derive(Copy, Clone)] +struct MyRef<'a> { + x: &'a (), + y: ::Assoc, +} + +impl Reborrow for MyMut<'_> {} + +impl<'a> CoerceShared> for MyMut<'a> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.stderr b/tests/ui/reborrow/coerce-shared-associated-type-field.stderr new file mode 100644 index 0000000000000..d533fa2eb8748 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-associated-type-field.rs:27:10 + | +LL | impl<'a> CoerceShared> for MyMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs new file mode 100644 index 0000000000000..3b6e9e25d8bb4 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs @@ -0,0 +1,26 @@ +#![feature(reborrow, decl_macro)] +#![allow(incomplete_features)] + +use std::marker::{CoerceShared, Reborrow}; + +macro my_macro($field:ident) { + pub struct MyMut<'a> { + $field: &'a i32, + field: &'a i64, + } + + #[derive(Clone, Copy)] + pub struct MyRef<'a> { + $field: &'a i32, + field: &'a i64, + } + + impl Reborrow for MyMut<'_> {} + + impl<'a> CoerceShared> for MyMut<'a> {} + //~^ ERROR +} + +my_macro!(field); + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr new file mode 100644 index 0000000000000..0b0b7bf048591 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr @@ -0,0 +1,13 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:14 + | +LL | impl<'a> CoerceShared> for MyMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | my_macro!(field); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `my_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-extra-marker.rs b/tests/ui/reborrow/coerce-shared-extra-marker.rs new file mode 100644 index 0000000000000..40026d68d5dca --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-extra-marker.rs @@ -0,0 +1,37 @@ +//@ run-pass + +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct MarkerExtraMut<'a, T> { + value: &'a mut T, + marker: PhantomData<&'a mut T>, +} + +impl<'a, T> Reborrow for MarkerExtraMut<'a, T> {} + +struct MarkerExtraRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for MarkerExtraRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for MarkerExtraRef<'a, T> {} + +impl<'a, T> CoerceShared> for MarkerExtraMut<'a, T> {} + +fn get<'a>(value: MarkerExtraRef<'a, i32>) -> &'a i32 { + value.value +} + +fn main() { + let mut value = 1; + let wrapped = MarkerExtraMut { value: &mut value, marker: PhantomData }; + assert_eq!(*get(wrapped), 1); +} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs new file mode 100644 index 0000000000000..d4558dd35d775 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct MyMut<'a> { + x: &'static (), + y: &'a (), +} + +impl Reborrow for MyMut<'_> {} + +#[derive(Copy, Clone)] +struct MyRef<'a> { + x: &'a (), + y: &'static (), +} + +impl<'a> CoerceShared> for MyMut<'a> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr new file mode 100644 index 0000000000000..bce87c68eb912 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-field-lifetime-swap.rs:18:10 + | +LL | impl<'a> CoerceShared> for MyMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs new file mode 100644 index 0000000000000..a687d2ccd7b0d --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-relations.rs @@ -0,0 +1,53 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct CustomMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for CustomMut<'a, T> {} + +#[derive(Clone, Copy)] +struct CustomRef<'a, T> { + value: &'a T, +} + +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +struct RenamedMut<'a, T> { + source: &'a mut T, +} + +impl<'a, T> Reborrow for RenamedMut<'a, T> {} + +#[derive(Clone, Copy)] +struct RenamedRef<'a, T> { + target: &'a T, +} + +impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} + +struct BadMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for BadMut<'a, T> {} + +#[derive(Clone, Copy)] +struct BadRef<'a, T> { + value: &'a u32, + _marker: std::marker::PhantomData, +} + +impl<'a, T> CoerceShared> for BadMut<'a, T> {} +//~^ ERROR + +fn good(_value: CustomRef<'_, u32>) {} + +fn main() { + let mut value = 1; + good(CustomMut { value: &mut value }); +} diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr new file mode 100644 index 0000000000000..a6ae9ab9524ec --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied + --> $DIR/coerce-shared-field-relations.rs:45:1 + | +LL | impl<'a, T> CoerceShared> for BadMut<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut T` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs new file mode 100644 index 0000000000000..3adda6733e16f --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs @@ -0,0 +1,20 @@ +//@ check-pass + +//@ aux-build: reborrow_foreign_private.rs + +#![feature(reborrow)] + +extern crate reborrow_foreign_private; + +use reborrow_foreign_private::ForeignRef; +use std::marker::{CoerceShared, Reborrow}; + +struct LocalMut<'a> { + value: &'a mut i32, +} + +impl<'a> Reborrow for LocalMut<'a> {} + +impl<'a> CoerceShared> for LocalMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs new file mode 100644 index 0000000000000..4a70fd49e38ff --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs @@ -0,0 +1,22 @@ +//@ check-pass + +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +mod foreign_ptr { + use std::marker::PhantomData; + + #[derive(Clone, Copy)] + pub struct ForeignPtrRef<'a>(*const i32, PhantomData<&'a ()>); +} + +use foreign_ptr::ForeignPtrRef; + +struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>); + +impl<'a> Reborrow for LocalPtrMut<'a> {} + +impl<'a> CoerceShared> for LocalPtrMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-generics.rs b/tests/ui/reborrow/coerce-shared-generics.rs new file mode 100644 index 0000000000000..9edab02835761 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-generics.rs @@ -0,0 +1,41 @@ +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct BufferMut<'a, T, U, const N: usize> { + data: &'a mut [T; N], + meta: U, +} + +impl<'a, T, U: Copy, const N: usize> Reborrow for BufferMut<'a, T, U, N> {} + +struct BufferRef<'a, T, U, const N: usize> { + data: &'a [T; N], + meta: U, +} + +impl<'a, T, U: Copy, const N: usize> Clone for BufferRef<'a, T, U, N> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T, U: Copy, const N: usize> Copy for BufferRef<'a, T, U, N> {} + +impl<'a, T, U: Copy, const N: usize> CoerceShared> +//~^ ERROR + for BufferMut<'a, T, U, N> +{ +} + +fn inspect(buffer: BufferRef<'_, u8, u16, N>) -> (usize, u16) { + (buffer.data.len(), buffer.meta) +} + +fn main() { + let mut data = [1, 2, 3, 4]; + let buffer = BufferMut { data: &mut data, meta: 9_u16 }; + + assert_eq!(inspect(buffer), (4, 9)); +} diff --git a/tests/ui/reborrow/coerce-shared-generics.stderr b/tests/ui/reborrow/coerce-shared-generics.stderr new file mode 100644 index 0000000000000..72efdd475fc10 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-generics.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-generics.rs:26:38 + | +LL | impl<'a, T, U: Copy, const N: usize> CoerceShared> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs new file mode 100644 index 0000000000000..90bb4be4c1894 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs @@ -0,0 +1,23 @@ +#![feature(reborrow)] + +// The impl is accepted, but using it to coerce a local marker into a `'static` +// target still requires the local borrow to live for `'static`. + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); + +impl<'a> Reborrow for CustomMarker<'a> {} + +#[derive(Clone, Copy)] +struct StaticMarkerRef<'a>(PhantomData<&'a ()>); + +impl<'a> CoerceShared> for CustomMarker<'a> {} + +fn method(_a: StaticMarkerRef<'static>) {} + +fn main() { + let a = CustomMarker(PhantomData); + method(a); + //~^ ERROR +} diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr new file mode 100644 index 0000000000000..337e4b6938944 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -0,0 +1,17 @@ +error[E0597]: `a` does not live long enough + --> $DIR/coerce-shared-lifetime-mismatch.rs:21:12 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | method(a); + | -------^- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` +LL | +LL | } + | - `a` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs new file mode 100644 index 0000000000000..c8db8fb206270 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.rs @@ -0,0 +1,22 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct MissingSourceMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for MissingSourceMut<'a, T> {} + +#[derive(Clone, Copy)] +struct MissingSourceRef<'a, T> { + value: &'a T, + len: usize, +} + +impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr new file mode 100644 index 0000000000000..5d5b8192836b1 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr @@ -0,0 +1,7 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-missing-target-field.rs:19:13 + | +LL | impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs new file mode 100644 index 0000000000000..f81e460e3a489 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -0,0 +1,45 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +trait Trait { + type Assoc; +} + +impl Trait for i32 { + type Assoc = u32; +} + +type AliasMutField<'a> = &'a mut ::Assoc; +type AliasRefField<'a> = &'a u32; + +struct AliasMut<'a> { + value: AliasMutField<'a>, +} + +impl Reborrow for AliasMut<'_> {} + +#[derive(Copy, Clone)] +struct AliasRef<'a> { + value: AliasRefField<'a>, +} + +impl<'a> CoerceShared> for AliasMut<'a> {} +//~^ ERROR + +struct InnerLifetimeMut<'a> { + value: &'a mut &'static (), +} + +impl Reborrow for InnerLifetimeMut<'_> {} + +#[derive(Copy, Clone)] +struct InnerLifetimeRef<'a> { + value: &'a &'a (), +} + +impl<'a> CoerceShared> for InnerLifetimeMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr new file mode 100644 index 0000000000000..f716fd7e63c0b --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `&'a mut u32: CoerceShared<&'a u32>` is not satisfied + --> $DIR/coerce-shared-mut-ref-field-validation.rs:29:1 + | +LL | impl<'a> CoerceShared> for AliasMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut u32` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs new file mode 100644 index 0000000000000..4f066079c749b --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs @@ -0,0 +1,51 @@ +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct ExtraMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for ExtraMut<'a, T> {} + +struct OmitMut<'a, T> { + value: &'a mut T, + extra: ExtraMut<'a, T>, +} + +impl<'a, T> Reborrow for OmitMut<'a, T> {} + +struct OmitRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for OmitRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for OmitRef<'a, T> {} + +impl<'a, T> CoerceShared> for OmitMut<'a, T> {} +//~^ ERROR + +fn read(value: OmitRef<'_, i32>) { + assert_eq!(*value.value, 1); +} + +fn main() { + let mut value = 1; + let mut extra_value = 2; + + { + let extra = ExtraMut { value: &mut extra_value }; + let wrapped = OmitMut { value: &mut value, extra }; + + read(wrapped); + } + + extra_value = 3; + assert_eq!(extra_value, 3); +} diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr new file mode 100644 index 0000000000000..70a0db88319a7 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:31:13 + | +LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs new file mode 100644 index 0000000000000..ade1890068d76 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs @@ -0,0 +1,53 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct ExtraMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for ExtraMut<'a, T> {} + +struct OmitMut<'a, T> { + value: &'a mut T, + extra: ExtraMut<'a, T>, +} + +impl<'a, T> Reborrow for OmitMut<'a, T> {} + +struct OmitRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for OmitRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for OmitRef<'a, T> {} + +impl<'a, T> CoerceShared> for OmitMut<'a, T> {} +//~^ ERROR + +fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { + value.value +} + +fn main() { + let mut value = 1; + let mut extra_value = 2; + let extra = ExtraMut { value: &mut extra_value }; + + let mut wrapped = OmitMut { + value: &mut value, + extra, + }; + + let shared = get(wrapped); + + *wrapped.extra.value = 3; + //~^ ERROR cannot assign to `*wrapped.extra.value` because it is borrowed + + let _ = shared; +} diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr new file mode 100644 index 0000000000000..d718103c80e7f --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr @@ -0,0 +1,21 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:13 + | +LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0506]: cannot assign to `*wrapped.extra.value` because it is borrowed + --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:49:5 + | +LL | let shared = get(wrapped); + | ------- `*wrapped.extra.value` is borrowed here +LL | +LL | *wrapped.extra.value = 3; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `*wrapped.extra.value` is assigned to here but it was already borrowed + | borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs new file mode 100644 index 0000000000000..55cc010c19af4 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs @@ -0,0 +1,45 @@ +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct ExtraMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for ExtraMut<'a, T> {} + +struct OmitMut<'a, T> { + value: &'a mut T, + extra: ExtraMut<'a, T>, +} + +impl<'a, T> Reborrow for OmitMut<'a, T> {} + +struct OmitRef<'a, T> { + value: &'a T, +} + +impl<'a, T> Clone for OmitRef<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for OmitRef<'a, T> {} + +impl<'a, T> CoerceShared> for OmitMut<'a, T> {} +//~^ ERROR + +fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { + value.value +} + +fn main() { + let mut value = 1; + let mut extra = 2; + let extra = ExtraMut { value: &mut extra }; + let wrapped = OmitMut { value: &mut value, extra }; + + assert_eq!(*get(wrapped), 1); +} diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr new file mode 100644 index 0000000000000..c663576228f62 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:13 + | +LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.rs b/tests/ui/reborrow/coerce-shared-reordered-field.rs new file mode 100644 index 0000000000000..f4630fe1f7d83 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-reordered-field.rs @@ -0,0 +1,32 @@ +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +struct ReorderMut<'a> { + a: &'a mut u8, + b: &'a mut u16, +} + +impl<'a> Reborrow for ReorderMut<'a> {} + +#[derive(Clone, Copy)] +struct ReorderRef<'a> { + b: &'a u16, + a: &'a u8, +} + +impl<'a> CoerceShared> for ReorderMut<'a> {} +//~^ ERROR + +fn read(value: ReorderRef<'_>) -> (u16, u8) { + (*value.b, *value.a) +} + +fn main() { + let mut a = 1; + let mut b = 2; + let wrapped = ReorderMut { a: &mut a, b: &mut b }; + + assert_eq!(read(wrapped), (2, 1)); +} diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.stderr b/tests/ui/reborrow/coerce-shared-reordered-field.stderr new file mode 100644 index 0000000000000..e3cd68547c428 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-reordered-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced + --> $DIR/coerce-shared-reordered-field.rs:19:10 + | +LL | impl<'a> CoerceShared> for ReorderMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs new file mode 100644 index 0000000000000..e56b6ad51c4bb --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.rs @@ -0,0 +1,23 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct GenericMut<'a, T, U> { + value: &'a mut T, + marker: PhantomData, +} + +impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {} + +#[derive(Clone, Copy)] +struct GenericRef<'a, T, U> { + value: &'a U, + marker: PhantomData, +} + +impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr new file mode 100644 index 0000000000000..3b07e0f64a212 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied + --> $DIR/coerce-shared-wrong-generic.rs:20:1 + | +LL | impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a U>` is not implemented for `&'a mut T` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 500436781a25d055375511d2c6c1d220b4263def Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Tue, 9 Jun 2026 01:08:45 +0200 Subject: [PATCH 44/71] Make Share::share final and improve docs --- library/core/src/clone.rs | 43 +++++++++++++------ library/core/src/lib.rs | 1 + .../share-trait/share-trait-final-method.rs | 21 +++++++++ .../share-trait-final-method.stderr | 11 +++++ 4 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 tests/ui/share-trait/share-trait-final-method.rs create mode 100644 tests/ui/share-trait/share-trait-final-method.stderr diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 93e774226349c..d03ec3b0066ed 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -293,21 +293,36 @@ pub macro Clone($item:item) { /// A trait for types whose [`Clone`] operation creates another alias to the same /// logical resource or shared state. /// -/// `Share` marks types where cloning creates another handle, reference, or alias -/// to the same logical resource or shared state, rather than an independent owned -/// value. The distinction is semantic, not cost-based: implementing `Share` does -/// not merely mean that cloning is cheap, constant-time, allocation-free, or -/// convenient. +/// `Share` refines the meaning of [`Clone`] for types where cloning a value +/// creates another handle, reference, or alias to the same logical resource or +/// shared state, rather than an independent owned value. The distinction is +/// semantic, not operational: `Share` does not mean merely that cloning is +/// cheap, constant-time, allocation-free, or convenient. /// -/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) -/// for implementors, but communicates that the resulting value aliases the same -/// underlying resource. +/// `Share` is a third way to think about creating another usable value: +/// +/// * [`Copy`] may duplicate a value implicitly. +/// * [`Clone`] explicitly creates another value. +/// * `Share` explicitly creates another value that aliases the same underlying +/// logical resource or shared state. +/// +/// `Share` is not a replacement for either [`Copy`] or [`Clone`], and neither +/// trait implies it. For example, integers are [`Copy`] but not `Share`, because +/// copying an integer creates an independent value. Likewise, not every cheap +/// [`Clone`] implementation is `Share`. /// /// Shared references, `Rc`, `Arc`, `Sender`, and `SyncSender` are /// examples of types that can be shared this way. Types such as `Vec`, -/// `String`, and `Box` are not `Share` even though they implement `Clone`, -/// because cloning them creates another owned value rather than another handle -/// to the same logical resource. +/// `String`, `Box`, owned collections, and similar owned values are not +/// `Share`, even though they implement [`Clone`], because cloning them creates +/// independent owned storage or value ownership. Mutable references (`&mut T`) +/// are not `Share`. +/// +/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) +/// for implementors, but communicates that the resulting value aliases the same +/// underlying resource. The `share` method is final, so implementors should +/// define the operation through [`Clone::clone`] and implement `Share` only when +/// those cloning semantics are clone-as-alias semantics. /// /// # Examples /// @@ -367,9 +382,11 @@ pub macro Clone($item:item) { pub trait Share: Clone { /// Creates another alias to the same underlying resource or shared state. /// - /// This is equivalent to calling [`Clone::clone`]. + /// This is equivalent to calling [`Clone::clone`]. Use `share` at call + /// sites to make aliasing intent explicit; implementors define this + /// operation through [`Clone::clone`], not by overriding this method. #[unstable(feature = "share_trait", issue = "156756")] - fn share(&self) -> Self { + final fn share(&self) -> Self { Clone::clone(self) } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b2ccb6994d676..192c5eff29e10 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -138,6 +138,7 @@ #![feature(f16)] #![feature(f128)] #![feature(field_projections)] +#![feature(final_associated_functions)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] diff --git a/tests/ui/share-trait/share-trait-final-method.rs b/tests/ui/share-trait/share-trait-final-method.rs new file mode 100644 index 0000000000000..35b383c1fe3a0 --- /dev/null +++ b/tests/ui/share-trait/share-trait-final-method.rs @@ -0,0 +1,21 @@ +#![feature(final_associated_functions)] +#![feature(share_trait)] + +use std::clone::Share; + +struct Alias; + +impl Clone for Alias { + fn clone(&self) -> Self { + Alias + } +} + +impl Share for Alias { + fn share(&self) -> Self { + //~^ ERROR cannot override `share` because it already has a `final` definition in the trait + Alias + } +} + +fn main() {} diff --git a/tests/ui/share-trait/share-trait-final-method.stderr b/tests/ui/share-trait/share-trait-final-method.stderr new file mode 100644 index 0000000000000..c20f3d1aefb62 --- /dev/null +++ b/tests/ui/share-trait/share-trait-final-method.stderr @@ -0,0 +1,11 @@ +error: cannot override `share` because it already has a `final` definition in the trait + --> $DIR/share-trait-final-method.rs:15:5 + | +LL | fn share(&self) -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `share` is marked final here + --> $SRC_DIR/core/src/clone.rs:LL:COL + +error: aborting due to 1 previous error + From 16e2fd2562658db3634e94e82174558265218465 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Tue, 9 Jun 2026 12:31:15 +0200 Subject: [PATCH 45/71] Remove redundant normalize-stderr directives --- .../corrected-field-mismatch-coerce-shared-issue-156315.rs | 1 - tests/ui/reborrow/coerce-shared-field-relations.rs | 2 -- tests/ui/reborrow/coerce-shared-missing-target-field.rs | 2 -- tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs | 2 -- tests/ui/reborrow/coerce-shared-wrong-generic.rs | 2 -- 5 files changed, 9 deletions(-) diff --git a/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs b/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs index b9721d5244311..59e76ec36ab90 100644 --- a/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs +++ b/tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs @@ -1,5 +1,4 @@ //@ known-bug: unknown -//@ normalize-stderr: "\n\n\z" -> "\n" #![feature(reborrow)] diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs index a687d2ccd7b0d..b2104efb6028d 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.rs +++ b/tests/ui/reborrow/coerce-shared-field-relations.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs index c8db8fb206270..c528fb85340ac 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.rs +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs index f81e460e3a489..9d87ff0bb938a 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs index e56b6ad51c4bb..2dc818a7015b2 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.rs +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; From 219a65fa36de679f3d9203de6fa168a82d987b4c Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Tue, 9 Jun 2026 18:06:54 +0200 Subject: [PATCH 46/71] Simplify the initialisation of region values in `region_infer` - `RegionInferenceContext::init_free_and_bound_regions()` is inlined into `::new()` and pulled to before initial SCC values are assigned - A couple of iteration-then-index patterns are changed into `iter_enumerated()`-style patterns - Some leftover code comments are removed and revised to actually describe the initialisation - Free regions are now marked as live everywhere in liveness constraints, then copied to scc_values upon initialisation, avoiding performing that logic twice It should do strictly less work, but almost all of it would only happen on free regions, and there shouldn't be large numbers of those. Depending on the cleverness of the optimiser most of the option/some checks should have been compiled away anyway. The key advantage as I see it is clearer flow: first liveness constraints are set up, then initial values are initialised, then region inference is ready to run. --- .../rustc_borrowck/src/region_infer/mod.rs | 117 +++++------------- .../rustc_borrowck/src/region_infer/values.rs | 21 ++-- compiler/rustc_index/src/interval.rs | 4 + 3 files changed, 44 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6bdb3ac14ee98..5f702ec65be75 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -345,7 +345,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { outlives_constraints, scc_annotations, type_tests, - liveness_constraints, + mut liveness_constraints, universe_causes, placeholder_indices, } = lowered_constraints; @@ -364,100 +364,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut scc_values = RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - for region in liveness_constraints.regions() { + // Initializes the region variables for each universally + // quantified region (lifetime parameter). The first N variables + // always correspond to the regions appearing in the function + // signature (both named and anonymous) and in where-clauses. + for (region, definition) in definitions.iter_enumerated() { let scc = constraint_sccs.scc(region); - scc_values.merge_liveness(scc, region, &liveness_constraints); - } - let mut result = Self { - definitions, - liveness_constraints, - constraints: outlives_constraints, - constraint_graph, - constraint_sccs, - scc_annotations, - universe_causes, - scc_values, - type_tests, - universal_region_relations, - }; - - result.init_free_and_bound_regions(); - - result - } - - /// Initializes the region variables for each universally - /// quantified region (lifetime parameter). The first N variables - /// always correspond to the regions appearing in the function - /// signature (both named and anonymous) and where-clauses. This - /// function iterates over those regions and initializes them with - /// minimum values. - /// - /// For example: - /// ```ignore (illustrative) - /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } - /// ``` - /// would initialize two variables like so: - /// ```ignore (illustrative) - /// R0 = { CFG, R0 } // 'a - /// R1 = { CFG, R0, R1 } // 'b - /// ``` - /// Here, R0 represents `'a`, and it contains (a) the entire CFG - /// and (b) any universally quantified regions that it outlives, - /// which in this case is just itself. R1 (`'b`) in contrast also - /// outlives `'a` and hence contains R0 and R1. - /// - /// This bit of logic also handles invalid universe relations - /// for higher-kinded types. - /// - /// We Walk each SCC `A` and `B` such that `A: B` - /// and ensure that universe(A) can see universe(B). - /// - /// This serves to enforce the 'empty/placeholder' hierarchy - /// (described in more detail on `RegionKind`): - /// - /// ```ignore (illustrative) - /// static -----+ - /// | | - /// empty(U0) placeholder(U1) - /// | / - /// empty(U1) - /// ``` - /// - /// In particular, imagine we have variables R0 in U0 and R1 - /// created in U1, and constraints like this; - /// - /// ```ignore (illustrative) - /// R1: !1 // R1 outlives the placeholder in U1 - /// R1: R0 // R1 outlives R0 - /// ``` - /// - /// Here, we wish for R1 to be `'static`, because it - /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. - /// - /// Thanks to this loop, what happens is that the `R1: R0` - /// constraint has lowered the universe of `R1` to `U0`, which in turn - /// means that the `R1: !1` constraint here will cause - /// `R1` to become `'static`. - fn init_free_and_bound_regions(&mut self) { - for variable in self.definitions.indices() { - let scc = self.constraint_sccs.scc(variable); - - match self.definitions[variable].origin { + match definition.origin { NllRegionVariableOrigin::FreeRegion => { // For each free, universally quantified region X: // Add all nodes in the CFG to liveness constraints - self.liveness_constraints.add_all_points(variable); - self.scc_values.add_all_points(scc); + liveness_constraints.add_all_points(region); // Add `end(X)` into the set for X. - self.scc_values.add_element(scc, variable); + scc_values.add_element(scc, region); } NllRegionVariableOrigin::Placeholder(placeholder) => { - self.scc_values.add_element(scc, placeholder); + // Placeholder region variables contain their placeholder. + scc_values.add_element(scc, placeholder); } NllRegionVariableOrigin::Existential { .. } => { @@ -465,6 +392,28 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } } + + for (region, liveness) in liveness_constraints.regions_and_liveness() { + // Initially copy the liveness constraints of any region that + // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. + // + // These values will later be propagated during [`Self::propagate_constraints()`] + scc_values.merge_liveness(constraint_sccs.scc(region), liveness); + // Note: this includes the liveness values we just initialised above! + } + + Self { + definitions, + liveness_constraints, + constraints: outlives_constraints, + constraint_graph, + constraint_sccs, + scc_annotations, + universe_causes, + scc_values, + type_tests, + universal_region_relations, + } } /// Returns an iterator over all the region indices. diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 626b7e1084192..5803134716506 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -96,8 +96,10 @@ impl LivenessValues { } /// Iterate through each region that has a value in this set. - pub(crate) fn regions(&self) -> impl Iterator { - self.points().rows() + pub(crate) fn regions_and_liveness( + &self, + ) -> impl Iterator)> { + self.points().iter_enumerated() } /// Iterate through each region that has a value in this set. @@ -303,11 +305,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { elem.add_to_row(self, r) } - /// Adds all the control-flow points to the values for `r`. - pub(crate) fn add_all_points(&mut self, r: N) { - self.points.insert_all_into_row(r); - } - /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to: /// r_from`). pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool { @@ -337,13 +334,9 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { Some(first_unset.index() - block.index()) } - /// `self[to] |= values[from]`, essentially: that is, take all the - /// elements for the region `from` from `values` and add them to - /// the region `to` in `self`. - pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) { - if let Some(set) = values.points().row(from) { - self.points.union_row(to, set); - } + /// Merge a row of liveness into our points. + pub(crate) fn merge_liveness(&mut self, to: N, liveness: &IntervalSet) { + self.points.union_row(to, liveness); } /// Returns `true` if `sup_region` contains all the CFG points that diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 716dd81d08fd4..b7b1531e50857 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -370,6 +370,10 @@ impl SparseIntervalMatrix { self.rows.get(row) } + pub fn iter_enumerated(&self) -> impl Iterator)> { + self.rows.iter_enumerated() + } + fn ensure_row(&mut self, row: R) -> &mut IntervalSet { self.rows.ensure_contains_elem(row, || IntervalSet::new(self.column_size)) } From 6a93bccd6020b202bffe6412fbb9cb5141b668c5 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 10 Jun 2026 09:28:47 +0800 Subject: [PATCH 47/71] ci: rename `job-macos` anchor to `job-macos-15` To make the distinction clearer versus `macos-26` runners in preparation of experimenting with running `macos-26` runners in parallel. --- src/ci/github-actions/jobs.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 2bdf83a9c006b..e71a7b86cbd9b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -22,7 +22,7 @@ runners: os: ubuntu-24.04-16core-64gb <<: *base-job - - &job-macos + - &job-macos-15 os: macos-15 # macOS 15 Arm64 <<: *base-job @@ -476,7 +476,7 @@ auto: DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-macos + <<: *job-macos-15 - name: dist-apple-various env: @@ -513,7 +513,7 @@ auto: MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer - <<: *job-macos + <<: *job-macos-15 - name: dist-aarch64-apple env: @@ -537,7 +537,7 @@ auto: DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-macos + <<: *job-macos-15 - name: aarch64-apple env: @@ -553,7 +553,7 @@ auto: # supports the hardware, so only need to test it there. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - <<: *job-macos + <<: *job-macos-15 ###################### # Windows Builders # From 3619e226cd19c59069de696c263fa6fbe9f2f9dd Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 10 Jun 2026 09:31:26 +0800 Subject: [PATCH 48/71] ci: add new anchor for `macos-26` runners --- src/ci/github-actions/jobs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index e71a7b86cbd9b..28412a3e92aa8 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -26,6 +26,10 @@ runners: os: macos-15 # macOS 15 Arm64 <<: *base-job + - &job-macos-26 + os: macos-26 # macOS 26 Arm64 + <<: *base-job + - &job-windows os: windows-2025 <<: *base-job From b509dcc7425f7353411c506fb767c99e80704949 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 10 Jun 2026 10:01:33 +0800 Subject: [PATCH 49/71] ci: create experiment test job `aarch64-apple-macos-26` Which mirrors the `aarch64-apple` `macos-15` runner image based workloads, but run on `macos-26` runner image, to evaluate running times. See RUST-157687. --- src/ci/github-actions/jobs.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 28412a3e92aa8..a555acfcd45a1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -559,6 +559,28 @@ auto: MACOSX_STD_DEPLOYMENT_TARGET: 11.0 <<: *job-macos-15 + # EXPERIMENT(#157687): we will try to run `aarch64-apple`-equivalent workload + # on `macos-26` runner images in parallel to evaluate the running times, since + # previous attempts have timed out multiple times. Remove/revert this job if + # this hangs or times out, or if it becomes the slowest Merge CI job, and let + # T-infra know. + - name: aarch64-apple-macos-26 + doc_url: https://github.com/rust-lang/rust/issues/157687 + env: + SCRIPT: > + ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin && + ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin src/tools/cargo + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer + # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else + # supports the hardware, so only need to test it there. + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + <<: *job-macos-26 + ###################### # Windows Builders # ###################### From 68381b1bd88e8068eea3392b2230bf6183300e77 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Wed, 10 Jun 2026 06:21:57 -0400 Subject: [PATCH 50/71] address clean code suggestion --- compiler/rustc_parse/src/parser/expr.rs | 39 ++++++++++--------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 781f2c21828dc..aa72068387b30 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1282,32 +1282,10 @@ impl<'a> Parser<'a> { let seq = match self.parse_expr_paren_seq() { Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))), Err(err) if self.is_expected_raw_ref_mut() => { - let err_span = self.prev_token.span.to(self.token.span); let guar = err.emit(); // Preserve the call expression so later passes can still diagnose the callee, // while treating the malformed `&raw ` argument as an error expression. - let mut args = thin_vec![self.mk_expr_err(err_span, guar)]; - let mut depth = 0usize; - loop { - if self.token == token::Eof { - break; - } else if depth == 0 && self.check(exp!(CloseParen)) { - self.bump(); - break; - } else if depth == 0 && self.check(exp!(Comma)) { - let comma_span = self.token.span; - self.bump(); - if !self.check(exp!(CloseParen)) { - args.push(self.mk_expr_err(comma_span.shrink_to_hi(), guar)); - } - continue; - } else if self.token.kind.open_delim().is_some() { - depth += 1; - } else if self.token.kind.close_delim().is_some() && depth > 0 { - depth -= 1; - } - self.bump(); - } + let args = self.recover_raw_ref_call_args(guar); return self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)); } Err(err) => Err(err), @@ -1318,6 +1296,21 @@ impl<'a> Parser<'a> { } } + fn recover_raw_ref_call_args(&mut self, guar: ErrorGuaranteed) -> ThinVec> { + let err_span = self.prev_token.span.to(self.token.span); + let mut args = thin_vec![self.mk_expr_err(err_span, guar)]; + while self.token != token::Eof && self.token != token::CloseParen { + if self.eat(exp!(Comma)) && self.token != token::Eof && self.token != token::CloseParen + { + args.push(self.mk_expr_err(self.prev_token.span.shrink_to_hi(), guar)); + } else { + self.parse_token_tree(); + } + } + let _ = self.eat(exp!(CloseParen)); + args + } + /// If we encounter a parser state that looks like the user has written a `struct` literal with /// parentheses instead of braces, recover the parser state and provide suggestions. #[instrument(skip(self, seq, snapshot), level = "trace")] From 0b6f400fe7155b537a27def31ea28914661d8af2 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 10 Jun 2026 15:29:58 +0200 Subject: [PATCH 51/71] `SccValues`: Inline and monomorphise `ToElementIndex` --- .../src/diagnostics/find_use.rs | 2 +- .../rustc_borrowck/src/region_infer/mod.rs | 15 +++-- .../region_infer/opaque_types/region_ctxt.rs | 6 +- .../rustc_borrowck/src/region_infer/values.rs | 59 ++++--------------- 4 files changed, 24 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/find_use.rs b/compiler/rustc_borrowck/src/diagnostics/find_use.rs index 96f48840468e5..ab4a710ba70e5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_use.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_use.rs @@ -35,7 +35,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> { queue.push_back(self.start_point); while let Some(p) = queue.pop_front() { - if !self.regioncx.region_contains(self.region_vid, p) { + if !self.regioncx.region_contains_point(self.region_vid, p) { continue; } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5f702ec65be75..c0b25b5b59f4b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -30,7 +30,7 @@ use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; -use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; +use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; use crate::type_check::Locations; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -379,12 +379,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { liveness_constraints.add_all_points(region); // Add `end(X)` into the set for X. - scc_values.add_element(scc, region); + scc_values.add_free_region(scc, region); } NllRegionVariableOrigin::Placeholder(placeholder) => { - // Placeholder region variables contain their placeholder. - scc_values.add_element(scc, placeholder); + scc_values.add_placeholder(scc, placeholder) } NllRegionVariableOrigin::Existential { .. } => { @@ -444,9 +443,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns `true` if the region `r` contains the point `p`. /// /// Panics if called before `solve()` executes, - pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex<'tcx>) -> bool { + pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { let scc = self.constraint_sccs.scc(r); - self.scc_values.contains(scc, p) + self.scc_values.contains_point(scc, p) } /// Returns the lowest statement index in `start..=end` which is not contained by `r`. @@ -1592,10 +1591,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.definitions[r] } - /// Check if the SCC of `r` contains `upper`. + /// Check if the SCC of `r` contains `upper`, a free region. pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { let r_scc = self.constraint_sccs.scc(r); - self.scc_values.contains(r_scc, upper) + self.scc_values.contains_free_region(r_scc, upper) } pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index ada8908e220ac..ae5a213c5c37d 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -78,11 +78,11 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { let placeholder_indices = Default::default(); let mut scc_values = RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - for variable in definitions.indices() { + for (variable, definition) in definitions.iter_enumerated() { let scc = constraint_sccs.scc(variable); - match definitions[variable].origin { + match definition.origin { NllRegionVariableOrigin::FreeRegion => { - scc_values.add_element(scc, variable); + scc_values.add_free_region(scc, variable); } _ => {} } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 5803134716506..5370d1c7537b3 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -298,13 +298,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { } } - /// Adds the given element to the value for the given region. Returns whether - /// the element is newly added (i.e., was not already present). - pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex<'tcx>) -> bool { - debug!("add(r={:?}, elem={:?})", r, elem); - elem.add_to_row(self, r) - } - /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to: /// r_from`). pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool { @@ -313,11 +306,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { | self.placeholders.union_rows(r_from, r_to) } - /// Returns `true` if the region `r` contains the given element. - pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex<'tcx>) -> bool { - elem.contained_in_row(self, r) - } - /// Returns the lowest statement index in `start..=end` which is not contained by `r`. pub(crate) fn first_non_contained_inclusive( &self, @@ -398,47 +386,26 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { pub(crate) fn region_value_str(&self, r: N) -> String { pretty_print_region_elements(self.elements_contained_in(r)) } -} - -pub(crate) trait ToElementIndex<'tcx>: Debug + Copy { - fn add_to_row(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool; - - fn contained_in_row(self, values: &RegionValues<'tcx, N>, row: N) -> bool; -} - -impl ToElementIndex<'_> for Location { - fn add_to_row(self, values: &mut RegionValues<'_, N>, row: N) -> bool { - let index = values.location_map.point_from_location(self); - values.points.insert(row, index) - } - fn contained_in_row(self, values: &RegionValues<'_, N>, row: N) -> bool { - let index = values.location_map.point_from_location(self); - values.points.contains(row, index) + /// Add a the free region with rvid `region` to SCC `scc` + pub(crate) fn add_free_region(&mut self, scc: N, region: RegionVid) { + self.free_regions.insert(scc, region); } -} -impl ToElementIndex<'_> for RegionVid { - fn add_to_row(self, values: &mut RegionValues<'_, N>, row: N) -> bool { - values.free_regions.insert(row, self) + pub(crate) fn add_placeholder(&mut self, scc: N, placeholder: ty::PlaceholderRegion<'tcx>) { + let index = self.placeholder_indices.lookup_index(placeholder); + self.placeholders.insert(scc, index); } - fn contained_in_row(self, values: &RegionValues<'_, N>, row: N) -> bool { - values.free_regions.contains(row, self) - } -} - -impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> { - fn add_to_row(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool { - let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); - let index = values.placeholder_indices.lookup_index(placeholder); - values.placeholders.insert(row, index) + /// Determine if `scc` contains the CFG point `p`. + pub(crate) fn contains_point(&self, scc: N, p: Location) -> bool { + let index = self.location_map.point_from_location(p); + self.points.contains(scc, index) } - fn contained_in_row(self, values: &RegionValues<'tcx, N>, row: N) -> bool { - let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); - let index = values.placeholder_indices.lookup_index(placeholder); - values.placeholders.contains(row, index) + /// Determine if `scc` contains the free region `free_region`. + pub(crate) fn contains_free_region(&self, scc: N, free_region: RegionVid) -> bool { + self.free_regions.contains(scc, free_region) } } From 0c141eb64bfd0fc6a45830c408038af583aa4c4b Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 9 Jun 2026 07:49:19 +0300 Subject: [PATCH 52/71] [Priroda] Port Priroda CLI tests to ui_test --- src/tools/miri/priroda/Cargo.lock | 454 +++++++++++++++++- src/tools/miri/priroda/Cargo.toml | 8 + src/tools/miri/priroda/tests/cli.rs | 169 ++----- .../tests/cli/continue_hits_breakpoint.stdin | 3 - .../tests/cli/continue_hits_breakpoint.stdout | 4 - .../cli/repeated_same_line_breakpoint.stdin | 4 - .../cli/repeated_same_line_breakpoint.stdout | 6 - .../tests/ui/continue_finishes_program.rs | 4 + .../continue_finishes_program.stdin | 0 .../continue_finishes_program.stdout | 0 .../tests/ui/continue_hits_breakpoint.rs | 7 + .../tests/ui/continue_hits_breakpoint.stdin | 3 + .../tests/ui/continue_hits_breakpoint.stdout | 4 + .../priroda/tests/ui/duplicate_breakpoint.rs | 4 + .../{cli => ui}/duplicate_breakpoint.stdin | 0 .../{cli => ui}/duplicate_breakpoint.stdout | 0 src/tools/miri/priroda/tests/ui/empty_main.rs | 5 + .../tests/{cli => ui}/empty_main.stdin | 0 .../tests/{cli => ui}/empty_main.stdout | 0 .../priroda/tests/ui/eof_exits_cleanly.rs | 5 + .../tests/{cli => ui}/eof_exits_cleanly.stdin | 0 .../{cli => ui}/eof_exits_cleanly.stdout | 0 .../miri/priroda/tests/ui/invalid_commands.rs | 5 + .../tests/{cli => ui}/invalid_commands.stdin | 0 .../tests/{cli => ui}/invalid_commands.stdout | 0 .../tests/ui/repeated_same_line_breakpoint.rs | 7 + .../ui/repeated_same_line_breakpoint.stdin | 4 + .../ui/repeated_same_line_breakpoint.stdout | 6 + .../tests/ui/source_step_changes_line.rs | 5 + .../tests/ui/source_step_changes_line.stdin | 3 + .../tests/ui/source_step_changes_line.stdout | 3 + .../miri/priroda/tests/ui/step_aliases.rs | 5 + .../tests/{cli => ui}/step_aliases.stdin | 0 .../tests/{cli => ui}/step_aliases.stdout | 0 34 files changed, 577 insertions(+), 141 deletions(-) delete mode 100644 src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin delete mode 100644 src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout delete mode 100644 src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin delete mode 100644 src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout create mode 100644 src/tools/miri/priroda/tests/ui/continue_finishes_program.rs rename src/tools/miri/priroda/tests/{cli => ui}/continue_finishes_program.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/continue_finishes_program.stdout (100%) create mode 100644 src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.rs create mode 100644 src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin create mode 100644 src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdout create mode 100644 src/tools/miri/priroda/tests/ui/duplicate_breakpoint.rs rename src/tools/miri/priroda/tests/{cli => ui}/duplicate_breakpoint.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/duplicate_breakpoint.stdout (100%) create mode 100644 src/tools/miri/priroda/tests/ui/empty_main.rs rename src/tools/miri/priroda/tests/{cli => ui}/empty_main.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/empty_main.stdout (100%) create mode 100644 src/tools/miri/priroda/tests/ui/eof_exits_cleanly.rs rename src/tools/miri/priroda/tests/{cli => ui}/eof_exits_cleanly.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/eof_exits_cleanly.stdout (100%) create mode 100644 src/tools/miri/priroda/tests/ui/invalid_commands.rs rename src/tools/miri/priroda/tests/{cli => ui}/invalid_commands.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/invalid_commands.stdout (100%) create mode 100644 src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.rs create mode 100644 src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin create mode 100644 src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdout create mode 100644 src/tools/miri/priroda/tests/ui/source_step_changes_line.rs create mode 100644 src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin create mode 100644 src/tools/miri/priroda/tests/ui/source_step_changes_line.stdout create mode 100644 src/tools/miri/priroda/tests/ui/step_aliases.rs rename src/tools/miri/priroda/tests/{cli => ui}/step_aliases.stdin (100%) rename src/tools/miri/priroda/tests/{cli => ui}/step_aliases.stdout (100%) diff --git a/src/tools/miri/priroda/Cargo.lock b/src/tools/miri/priroda/Cargo.lock index 25c97f8919423..2eb63f8a16b3b 100644 --- a/src/tools/miri/priroda/Cargo.lock +++ b/src/tools/miri/priroda/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aes" version = "0.9.0" @@ -13,6 +28,31 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.102" @@ -25,6 +65,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + [[package]] name = "bincode" version = "1.3.3" @@ -40,12 +95,32 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + [[package]] name = "capstone" version = "0.14.0" @@ -65,6 +140,29 @@ dependencies = [ "cc", ] +[[package]] +name = "cargo-platform" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "cc" version = "1.2.62" @@ -127,6 +225,60 @@ dependencies = [ "inout", ] +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "comma" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys", +] + [[package]] name = "cpubits" version = "0.1.1" @@ -187,6 +339,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" @@ -203,6 +361,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -288,6 +456,12 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "hashbrown" version = "0.15.5" @@ -324,6 +498,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + [[package]] name = "indexmap" version = "2.14.0" @@ -336,6 +516,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "indicatif" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +dependencies = [ + "console", + "portable-atomic", + "unicode-width", + "unit-prefix", + "web-time", +] + [[package]] name = "inout" version = "0.2.2" @@ -381,12 +574,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.186" @@ -481,6 +686,15 @@ dependencies = [ "libc", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.2.0" @@ -537,6 +751,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -549,6 +772,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + [[package]] name = "parking_lot" version = "0.12.5" @@ -605,6 +834,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -614,6 +849,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettydiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac17546d82912e64874e3d5b40681ce32eac4e5834344f51efcf689ff1550a65" +dependencies = [ + "owo-colors", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -629,6 +873,8 @@ name = "priroda" version = "0.1.0" dependencies = [ "miri", + "regex", + "ui_test", ] [[package]] @@ -724,15 +970,71 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 2.0.18", ] +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustfix" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a" +dependencies = [ + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "rustix" version = "1.1.4" @@ -763,6 +1065,10 @@ name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] [[package]] name = "serde" @@ -807,6 +1113,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -831,6 +1146,17 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "spanned" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d" +dependencies = [ + "anyhow", + "bstr", + "color-eyre", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -861,13 +1187,33 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -881,24 +1227,112 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ui_test" +version = "0.30.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c8811281d587a786747c0c49245925016c07767bc996305bdd34d5ce076786a" +dependencies = [ + "annotate-snippets", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned", +] + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + [[package]] name = "uuid" version = "1.23.1" @@ -910,6 +1344,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1013,6 +1453,16 @@ dependencies = [ "semver", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows" version = "0.61.3" diff --git a/src/tools/miri/priroda/Cargo.toml b/src/tools/miri/priroda/Cargo.toml index 084773f10e660..8ecb57a73e3bf 100644 --- a/src/tools/miri/priroda/Cargo.toml +++ b/src/tools/miri/priroda/Cargo.toml @@ -12,8 +12,16 @@ name = "priroda" path = "src/main.rs" doctest = false # and no doc tests +[[test]] +name = "cli" +harness = false + [dependencies] miri = { path = ".." } [package.metadata.rust-analyzer] rustc_private = true + +[dev-dependencies] +ui_test = "0.30.2" +regex = "1.5.5" diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index 77d76c3e1d7e9..d818ee70a1115 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -1,132 +1,57 @@ -use std::io::Write; +use std::env; use std::path::PathBuf; -use std::process::{Command, Stdio}; +use std::process::Command; -/// Runs one scripted Priroda CLI fixture against a Rust program. -/// -/// `test_path` is the extensionless fixture stem. The helper sends -/// `.stdin` to Priroda and expects exact stdout from -/// `.stdout`. -fn run_cli_test(program_path: &str, test_path: &str) -> Result<(), Box> { - // Anchor relative paths to the crate root so the test does not depend on - // the current working directory used by a test runner or IDE. - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - - let test_path = manifest_dir.join(test_path); - let stdin_path = test_path.with_extension("stdin"); - let stdout_path = test_path.with_extension("stdout"); +use regex::bytes::Regex; +use ui_test::spanned::Spanned; +use ui_test::status_emitter::StatusEmitter; +use ui_test::{CommandBuilder, Config, default_file_filter, run_tests_generic}; - // Keep scripted stdin as raw bytes: the helper only forwards it to the - // child process, so there is no need to validate it as UTF-8 here. - let input = std::fs::read(stdin_path)?; +fn main() -> Result<(), Box> { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let miri_dir = manifest_dir.parent().unwrap(); - // Source paths printed for `std` frames come from the active rustc - // toolchain, not from `MIRI_SYSROOT`, so expand `{RUSTC_SYSROOT}` from - // `rustc --print sysroot`. Use strict UTF-8 because this value is part of - // the exact stdout contract; invalid output should fail the test. let rustc_sysroot = Command::new("rustc").arg("--print").arg("sysroot").output()?; let rustc_sysroot = String::from_utf8(rustc_sysroot.stdout)?.trim().to_owned(); - // Keep fixture stdout exact while allowing machine-specific absolute paths - // to be written with stable placeholders. - let expected_output = std::fs::read_to_string(stdout_path)? - .replace("{MANIFEST_DIR}", &manifest_dir.display().to_string()) - .replace("{MIRI_DIR}", &manifest_dir.parent().unwrap().display().to_string()) - .replace("{RUSTC_SYSROOT}", &rustc_sysroot); - - let mut priroda = Command::new(env!("CARGO_BIN_EXE_priroda")) - .arg(manifest_dir.join(program_path)) - // The CLI contract checks stderr exactly, so inherited logging - // configuration would make the test fail for reasons unrelated to - // Priroda's behavior. - .env_remove("RUSTC_LOG") - .env_remove("RUST_LOG") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - - priroda.stdin.as_mut().unwrap().write_all(&input)?; - // Close stdin after the scripted input so Priroda can observe EOF if a - // fixture does not explicitly quit. - drop(priroda.stdin.take()); - - let output = priroda.wait_with_output()?; - - assert!( - output.status.success(), - "priroda exited with status {}\nstderr:\n{}", - output.status, - // This is only diagnostic text for a failed assertion, so lossy UTF-8 is - // better than hiding stderr behind a second conversion failure. - String::from_utf8_lossy(&output.stderr), - ); - - assert!( - output.stderr.is_empty(), - "expected no stderr output, got:\n{}", - // Same reasoning as above: stderr is not part of the success path here; - // it is shown to make a failure easier to debug. - String::from_utf8_lossy(&output.stderr), - ); - - // Actual stdout is the value under test, so require valid UTF-8 before doing - // the exact string comparison against the expanded fixture. - assert_eq!(String::from_utf8(output.stdout)?, expected_output); + let mut program = CommandBuilder::rustc(); + program.program = PathBuf::from(env!("CARGO_BIN_EXE_priroda")); + + // Remove logging env vars that might leak into stderr + program.envs.push(("RUSTC_LOG".into(), None)); + program.envs.push(("RUST_LOG".into(), None)); + + let mut config = Config { + program, + out_dir: PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("priroda_ui"), + ..Config::rustc("tests/ui") + }; + + // Replace the dynamic paths in the actual stdout with the stable placeholders + let manifest_dir_regex = + Regex::new(®ex::escape(&manifest_dir.display().to_string())).unwrap(); + let miri_dir_regex = Regex::new(®ex::escape(&miri_dir.display().to_string())).unwrap(); + let rustc_sysroot_regex = Regex::new(®ex::escape(&rustc_sysroot)).unwrap(); + + config.comment_defaults.base().normalize_stdout.extend([ + (manifest_dir_regex.into(), b"{MANIFEST_DIR}".to_vec()), + (miri_dir_regex.into(), b"{MIRI_DIR}".to_vec()), + (rustc_sysroot_regex.into(), b"{RUSTC_SYSROOT}".to_vec()), + ]); + + // Priroda CLI tests do not currently require annotation comments in the test files + config.comment_defaults.base().exit_status = Spanned::dummy(0).into(); + config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); + + let args = ui_test::Args::test()?; + config.with_args(&args); + + run_tests_generic( + vec![config], + default_file_filter, + |_, _| {}, + Box::::from(args.format), + )?; Ok(()) } - -/// Verifies Priroda can start on the simplest passing Rust program and accept -/// a scripted `quit` command. -#[test] -fn empty_main() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/empty_main") -} - -/// Verifies EOF exits the debugger loop cleanly without requiring an explicit -/// quit command. -#[test] -fn eof_exits_cleanly() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/eof_exits_cleanly") -} - -/// Verifies unknown commands and malformed breakpoints are rejected without -/// mutating debugger state. -#[test] -fn invalid_commands() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/invalid_commands") -} - -/// Verifies breakpoint aliases and duplicate detection before execution starts. -#[test] -fn duplicate_breakpoint() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/duplicate_breakpoint") -} - -/// Verifies continue can drive the interpreted program to normal completion. -#[test] -fn continue_finishes_program() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/continue_finishes_program") -} - -/// Verifies continue stops when execution reaches a registered source-location -/// breakpoint. -#[test] -fn continue_hits_breakpoint() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/continue_hits_breakpoint") -} - -/// Verifies every current spelling of MIR-instruction stepping advances -/// execution and reports a location. -#[test] -fn step_aliases() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/step_aliases") -} - -/// Documents the current repeated-stop behavior when multiple MIR locations map -/// to the same source breakpoint line. -#[test] -fn repeated_same_line_breakpoint() -> Result<(), Box> { - run_cli_test("../tests/pass/empty_main.rs", "tests/cli/repeated_same_line_breakpoint") -} diff --git a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin deleted file mode 100644 index 812cbe8eb2188..0000000000000 --- a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdin +++ /dev/null @@ -1,3 +0,0 @@ -break ../tests/pass/empty_main.rs:3 -continue -quit diff --git a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout b/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout deleted file mode 100644 index 50a0905ec56cb..0000000000000 --- a/src/tools/miri/priroda/tests/cli/continue_hits_breakpoint.stdout +++ /dev/null @@ -1,4 +0,0 @@ -(priroda) breakpoint added: {MIRI_DIR}/tests/pass/empty_main.rs:3 -(priroda) Hit breakpoint -{MIRI_DIR}/tests/pass/empty_main.rs:3 -(priroda) quitting diff --git a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin deleted file mode 100644 index ac38e96931ba1..0000000000000 --- a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdin +++ /dev/null @@ -1,4 +0,0 @@ -break ../tests/pass/empty_main.rs:3 -continue -continue -quit diff --git a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout b/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout deleted file mode 100644 index 106b53598992d..0000000000000 --- a/src/tools/miri/priroda/tests/cli/repeated_same_line_breakpoint.stdout +++ /dev/null @@ -1,6 +0,0 @@ -(priroda) breakpoint added: {MIRI_DIR}/tests/pass/empty_main.rs:3 -(priroda) Hit breakpoint -{MIRI_DIR}/tests/pass/empty_main.rs:3 -(priroda) Hit breakpoint -{MIRI_DIR}/tests/pass/empty_main.rs:3 -(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_program.rs b/src/tools/miri/priroda/tests/ui/continue_finishes_program.rs new file mode 100644 index 0000000000000..e187158df4b8b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_program.rs @@ -0,0 +1,4 @@ +// Verifies continue can drive the interpreted program to normal completion. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/continue_finishes_program.stdin rename to src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin diff --git a/src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/continue_finishes_program.stdout rename to src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout diff --git a/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.rs b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.rs new file mode 100644 index 0000000000000..756e00b859fce --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.rs @@ -0,0 +1,7 @@ +// Verifies continue stops when execution reaches a registered source-location +// breakpoint. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() { + let _value = 0; +} diff --git a/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin new file mode 100644 index 0000000000000..f7ad7e7ed0ea9 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin @@ -0,0 +1,3 @@ +break tests/ui/continue_hits_breakpoint.rs:6 +continue +quit diff --git a/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdout b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdout new file mode 100644 index 0000000000000..15cae3bed56d5 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdout @@ -0,0 +1,4 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/continue_hits_breakpoint.rs:6 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/continue_hits_breakpoint.rs:6 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/duplicate_breakpoint.rs b/src/tools/miri/priroda/tests/ui/duplicate_breakpoint.rs new file mode 100644 index 0000000000000..dfd33a0de8611 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/duplicate_breakpoint.rs @@ -0,0 +1,4 @@ +// Verifies breakpoint aliases and duplicate detection before execution starts. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin b/src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdin rename to src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdin diff --git a/src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout b/src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/duplicate_breakpoint.stdout rename to src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdout diff --git a/src/tools/miri/priroda/tests/ui/empty_main.rs b/src/tools/miri/priroda/tests/ui/empty_main.rs new file mode 100644 index 0000000000000..2e103198f3649 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/empty_main.rs @@ -0,0 +1,5 @@ +// Verifies Priroda can start on the simplest passing Rust program and accept +// a scripted `quit` command. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/empty_main.stdin b/src/tools/miri/priroda/tests/ui/empty_main.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/empty_main.stdin rename to src/tools/miri/priroda/tests/ui/empty_main.stdin diff --git a/src/tools/miri/priroda/tests/cli/empty_main.stdout b/src/tools/miri/priroda/tests/ui/empty_main.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/empty_main.stdout rename to src/tools/miri/priroda/tests/ui/empty_main.stdout diff --git a/src/tools/miri/priroda/tests/ui/eof_exits_cleanly.rs b/src/tools/miri/priroda/tests/ui/eof_exits_cleanly.rs new file mode 100644 index 0000000000000..84ce4c91f7e73 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/eof_exits_cleanly.rs @@ -0,0 +1,5 @@ +// Verifies EOF exits the debugger loop cleanly without requiring an explicit +// quit command. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdin b/src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdin rename to src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdin diff --git a/src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout b/src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/eof_exits_cleanly.stdout rename to src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdout diff --git a/src/tools/miri/priroda/tests/ui/invalid_commands.rs b/src/tools/miri/priroda/tests/ui/invalid_commands.rs new file mode 100644 index 0000000000000..f869316a199b7 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/invalid_commands.rs @@ -0,0 +1,5 @@ +// Verifies unknown commands and malformed breakpoints are rejected without +// mutating debugger state. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/invalid_commands.stdin b/src/tools/miri/priroda/tests/ui/invalid_commands.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/invalid_commands.stdin rename to src/tools/miri/priroda/tests/ui/invalid_commands.stdin diff --git a/src/tools/miri/priroda/tests/cli/invalid_commands.stdout b/src/tools/miri/priroda/tests/ui/invalid_commands.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/invalid_commands.stdout rename to src/tools/miri/priroda/tests/ui/invalid_commands.stdout diff --git a/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.rs b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.rs new file mode 100644 index 0000000000000..4a1247ab37b4a --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.rs @@ -0,0 +1,7 @@ +// Documents the current repeated-stop behavior when multiple MIR locations map +// to the same source breakpoint line. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() { + let _value = 0; +} diff --git a/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin new file mode 100644 index 0000000000000..a179d469cc2ba --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin @@ -0,0 +1,4 @@ +break tests/ui/repeated_same_line_breakpoint.rs:6 +continue +continue +quit diff --git a/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdout b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdout new file mode 100644 index 0000000000000..27ba93d504ed4 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdout @@ -0,0 +1,6 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/source_step_changes_line.rs b/src/tools/miri/priroda/tests/ui/source_step_changes_line.rs new file mode 100644 index 0000000000000..796b193009841 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/source_step_changes_line.rs @@ -0,0 +1,5 @@ +// Verifies source-level stepping advances until the displayed source location +// changes. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin b/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin new file mode 100644 index 0000000000000..955048258c669 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin @@ -0,0 +1,3 @@ +s +step +quit diff --git a/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdout b/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdout new file mode 100644 index 0000000000000..bd2d2487d316b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/source_step_changes_line.stdout @@ -0,0 +1,3 @@ +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/step_aliases.rs b/src/tools/miri/priroda/tests/ui/step_aliases.rs new file mode 100644 index 0000000000000..694b1be4f49cd --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/step_aliases.rs @@ -0,0 +1,5 @@ +// Verifies every current spelling of MIR-instruction stepping advances +// execution and reports a location. +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/priroda/tests/cli/step_aliases.stdin b/src/tools/miri/priroda/tests/ui/step_aliases.stdin similarity index 100% rename from src/tools/miri/priroda/tests/cli/step_aliases.stdin rename to src/tools/miri/priroda/tests/ui/step_aliases.stdin diff --git a/src/tools/miri/priroda/tests/cli/step_aliases.stdout b/src/tools/miri/priroda/tests/ui/step_aliases.stdout similarity index 100% rename from src/tools/miri/priroda/tests/cli/step_aliases.stdout rename to src/tools/miri/priroda/tests/ui/step_aliases.stdout From 50b0362bf87c5f5a2d5b73920b0d905bf1b8cc6b Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Tue, 9 Jun 2026 16:08:14 -0300 Subject: [PATCH 53/71] Require #[pin_v2] for explicit pin-projection patterns The structural-pinning check only ran for the implicit projection (match ergonomics, gated on `default_binding_modes`). Explicit `&pin mut` / `ref pin` patterns went through `check_pat_ref` and the tuple-struct/struct paths without any check, so they could pin-project through a type that never opted into structural pinning and hand out a `Pin<&mut Field>` for it, breaking the `Pin` guarantee. Move the check to the destructuring site (`check_pin_projection`, called from `check_pat_struct` and `check_pat_tuple_struct`) so one check covers both the implicit and explicit projections. --- compiler/rustc_hir_typeck/src/pat.rs | 52 ++++++--- .../auxiliary/non_pin_project.rs | 3 + .../pin-pattern-foreign-non-pin-project.rs | 22 ++++ ...pin-pattern-foreign-non-pin-project.stderr | 8 ++ .../pin-pattern-non-pin-project.rs | 61 ++++++++++ .../pin-pattern-non-pin-project.stderr | 104 ++++++++++++++++++ .../ui/pin-ergonomics/user-type-projection.rs | 1 + 7 files changed, 236 insertions(+), 15 deletions(-) create mode 100644 tests/ui/pin-ergonomics/auxiliary/non_pin_project.rs create mode 100644 tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.rs create mode 100644 tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr create mode 100644 tests/ui/pin-ergonomics/pin-pattern-non-pin-project.rs create mode 100644 tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b5ae3d06b3b33..ae716e4dc6870 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -544,21 +544,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); - // if the inner_ty is an ADT, make sure that it can be structurally pinned - // (i.e., it is `#[pin_v2]`). - if let Some(adt) = inner_ty.ty_adt_def() - && !adt.is_pin_project() - && !adt.is_pin() - { - let def_span: Option = self.tcx.hir_span_if_local(adt.did()); - let sugg_span = def_span.map(|span| span.shrink_to_lo()); - self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { - span: pat.span, - def_span, - sugg_span, - }); - } - // Use the old pat info to keep `current_depth` to its old value. let new_pat_info = self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info); @@ -1523,6 +1508,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) } + /// Reject pin-projection through a type that isn't structurally pinnable. + /// + /// Destructuring an ADT underneath a `&pin` reference projects its fields as pinned references. + /// This is only sound if the type opted into structural pinning with `#[pin_v2]`; otherwise it + /// would let safe code form a `Pin<&mut Field>` for a type that should never be pinned, breaking + /// the `Pin` guarantee (see #157634). + /// + /// This covers both explicit (`&pin mut`/`&pin const`) and implicit (match-ergonomics) + /// projection. `max_pinnedness` is only set for `&pin mut`, so the implicit shared (`&pin + /// const`) case is instead recognized through its pinned binding mode, hence both are checked. + fn check_pin_projection( + &self, + pat: &'tcx Pat<'tcx>, + pat_ty: Ty<'tcx>, + pat_info: PatInfo<'tcx>, + ) { + let through_pin = pat_info.max_pinnedness == PinnednessCap::Pinned + || matches!(pat_info.binding_mode, ByRef::Yes(Pinnedness::Pinned, _)); + if through_pin + && let Some(adt) = pat_ty.ty_adt_def() + && !adt.is_pin_project() + && !adt.is_pin() + { + let def_span: Option = self.tcx.hir_span_if_local(adt.did()); + let sugg_span = def_span.map(|span| span.shrink_to_lo()); + self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { + span: pat.span, + def_span, + sugg_span, + }); + } + } + fn check_pat_struct( &self, pat: &'tcx Pat<'tcx>, @@ -1533,6 +1551,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { + self.check_pin_projection(pat, pat_ty, pat_info); + // Type-check the path. let had_err = self.demand_eqtype_pat(pat.span, expected, pat_ty, &pat_info.top_info); @@ -1791,6 +1811,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { + self.check_pin_projection(pat, pat_ty, pat_info); + let tcx = self.tcx; let on_error = |e| { for pat in subpats { diff --git a/tests/ui/pin-ergonomics/auxiliary/non_pin_project.rs b/tests/ui/pin-ergonomics/auxiliary/non_pin_project.rs new file mode 100644 index 0000000000000..75a97aaa6a53f --- /dev/null +++ b/tests/ui/pin-ergonomics/auxiliary/non_pin_project.rs @@ -0,0 +1,3 @@ +// A plain, non-`#[pin_v2]` type defined in another crate, so it has no local span in the +// downstream crate that projects through it. +pub struct Foreign(pub T); diff --git a/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.rs b/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.rs new file mode 100644 index 0000000000000..d968e4394c079 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.rs @@ -0,0 +1,22 @@ +//@ edition:2024 +//@ aux-build:non_pin_project.rs +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// Regression test for #157634, exercising the diagnostic good-practice raised in the #157542 +// review: the projection error must be emitted even when the projected-through type comes from +// another crate and therefore has no local span. `ProjectOnNonPinProjectType` carries its +// `def_span`/`sugg_span` as `Option`, so for a foreign type the "type defined here" note +// and the `#[pin_v2]` suggestion are dropped rather than suppressing the error itself. + +extern crate non_pin_project; + +use non_pin_project::Foreign; +use std::pin::Pin; + +fn project(p: Pin<&mut Foreign>) { + let &pin mut Foreign(ref pin mut _x) = p; + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr b/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr new file mode 100644 index 0000000000000..493de31d88240 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr @@ -0,0 +1,8 @@ +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-foreign-non-pin-project.rs:18:18 + | +LL | let &pin mut Foreign(ref pin mut _x) = p; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.rs b/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.rs new file mode 100644 index 0000000000000..a51bbe08eab94 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.rs @@ -0,0 +1,61 @@ +//@ edition:2024 +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// Regression test for #157634. +// +// The implicit pin-projection (via match ergonomics) is only allowed on `#[pin_v2]` types, but +// the explicit `&pin mut` / `ref pin` pattern forms used to project through *any* type. That is +// unsound: it lets safe code form a `Pin<&mut Field>` for a type that never opted into structural +// pinning, breaking the `Pin` guarantee. Check that the explicit forms are now gated the same way +// as the implicit one. + +use std::pin::Pin; + +struct NotPinProject(T); + +struct NotPinProjectStruct { + x: T, +} + +enum NotPinProjectEnum { + Tuple(T), + Struct { x: T }, +} + +fn tuple_struct(p: Pin<&mut NotPinProject>) { + let &pin mut NotPinProject(ref pin mut _x) = p; + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn struct_field(p: Pin<&mut NotPinProjectStruct>) { + let &pin mut NotPinProjectStruct { x: ref pin mut _x } = p; + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn shared(p: Pin<&NotPinProject>) { + let &pin const NotPinProject(ref pin const _x) = p; + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn enum_tuple(p: Pin<&mut NotPinProjectEnum>) { + if let &pin mut NotPinProjectEnum::Tuple(ref pin mut _x) = p {} + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn enum_struct(p: Pin<&mut NotPinProjectEnum>) { + if let &pin mut NotPinProjectEnum::Struct { x: ref pin mut _x } = p {} + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +// The exact shape from the issue: `Thing` unconditionally implements `Unpin`, so it must not be +// possible to project a pinned reference to one of its fields. +struct Thing(T); +impl Unpin for Thing {} + +fn issue_157634(pinned_thing: Pin<&mut Thing>>) { + let &pin mut Thing(ref pin mut _pinned_option) = pinned_thing; + //~^ ERROR cannot project on type that is not `#[pin_v2]` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr b/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr new file mode 100644 index 0000000000000..c0fe4428ea53d --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr @@ -0,0 +1,104 @@ +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:27:18 + | +LL | let &pin mut NotPinProject(ref pin mut _x) = p; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:15:1 + | +LL | struct NotPinProject(T); + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotPinProject(T); + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:32:18 + | +LL | let &pin mut NotPinProjectStruct { x: ref pin mut _x } = p; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:17:1 + | +LL | struct NotPinProjectStruct { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotPinProjectStruct { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:37:20 + | +LL | let &pin const NotPinProject(ref pin const _x) = p; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:15:1 + | +LL | struct NotPinProject(T); + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotPinProject(T); + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:42:21 + | +LL | if let &pin mut NotPinProjectEnum::Tuple(ref pin mut _x) = p {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:21:1 + | +LL | enum NotPinProjectEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | enum NotPinProjectEnum { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:47:21 + | +LL | if let &pin mut NotPinProjectEnum::Struct { x: ref pin mut _x } = p {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:21:1 + | +LL | enum NotPinProjectEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | enum NotPinProjectEnum { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pin-pattern-non-pin-project.rs:57:18 + | +LL | let &pin mut Thing(ref pin mut _pinned_option) = pinned_thing; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pin-pattern-non-pin-project.rs:53:1 + | +LL | struct Thing(T); + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct Thing(T); + | + +error: aborting due to 6 previous errors + diff --git a/tests/ui/pin-ergonomics/user-type-projection.rs b/tests/ui/pin-ergonomics/user-type-projection.rs index f482586b6ebcc..4e8ef9887425e 100644 --- a/tests/ui/pin-ergonomics/user-type-projection.rs +++ b/tests/ui/pin-ergonomics/user-type-projection.rs @@ -9,6 +9,7 @@ // Historically, this could occur when the code handling those projections did not know // about `&pin` patterns, and incorrectly treated them as plain `&`/`&mut` patterns instead. +#[pin_v2] struct Data { x: u32 } From 3b17418868502bb003ac1f5f08977f9697dd74aa Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 11 Jun 2026 10:51:15 +0300 Subject: [PATCH 54/71] [Priroda] Add a `--bless` flag to update the expected output files --- src/tools/miri/priroda/Cargo.toml | 1 + src/tools/miri/priroda/README.md | 12 ++++++++++++ src/tools/miri/priroda/tests/cli.rs | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/priroda/Cargo.toml b/src/tools/miri/priroda/Cargo.toml index 8ecb57a73e3bf..88e65653449c8 100644 --- a/src/tools/miri/priroda/Cargo.toml +++ b/src/tools/miri/priroda/Cargo.toml @@ -10,6 +10,7 @@ edition = "2024" [[bin]] name = "priroda" path = "src/main.rs" +test = false # Disable unit tests to avoid errors with --bless doctest = false # and no doc tests [[test]] diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 6fe4ca23d59ed..e0dafd2685504 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -43,6 +43,18 @@ Priroda's CLI tests also need `MIRI_SYSROOT`. Run them from `miri/priroda/`: cargo test ``` +If the CLI tests fail due to mismatched output, you can update the expected output files by running the tests with the `--bless` flag: + +```sh +cargo test -- --bless +``` + +or + +```sh +RUSTC_BLESS=1 cargo test +``` + ## Commands | Command | Description | diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index d818ee70a1115..f4f9bf5d176ac 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -43,7 +43,8 @@ fn main() -> Result<(), Box> { config.comment_defaults.base().exit_status = Spanned::dummy(0).into(); config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); - let args = ui_test::Args::test()?; + let mut args = ui_test::Args::test()?; + args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); config.with_args(&args); run_tests_generic( From 84c2a742022f8719bd5502a1b26fa6efeb2b6c0b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Jun 2026 13:34:33 +0200 Subject: [PATCH 55/71] add borrow model test for mixed-mutability static --- .../both_borrows/mixed_mutability_static.rs | 16 ++++++++++++++++ .../mixed_mutability_static.stack.stderr | 18 ++++++++++++++++++ .../mixed_mutability_static.tree.stderr | 19 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.rs create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.stack.stderr create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.tree.stderr diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.rs b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.rs new file mode 100644 index 0000000000000..553c79e6270f6 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.rs @@ -0,0 +1,16 @@ +//! Ensure that we do not permit mutating the part of an interior mutable static +//! that is outside the `Cell`. + +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +use std::sync::atomic::*; + +static X: (i32, AtomicI32) = (0, AtomicI32::new(1)); + +fn main() { + let ptr = &raw const X; + unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; + //~[stack]^ERROR: that tag only grants SharedReadOnly permission + //~[tree]|ERROR: /write access .* is forbidden/ +} diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.stack.stderr b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.stack.stderr new file mode 100644 index 0000000000000..2af796bbf9619 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.stack.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: attempting a write access using at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location + --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC + | +LL | unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x8] + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadOnly retag at offsets [0x0..0x4] + --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC + | +LL | let ptr = &raw const X; + | ^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.tree.stderr new file mode 100644 index 0000000000000..5e8cb33edf2da --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.tree.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC + | +LL | unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag has state Frozen which forbids this child write access +help: the accessed tag was created here, in the initial state Cell + --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC + | +LL | let ptr = &raw const X; + | ^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 334c9df2ebf4f419406e67f53a97e7fde274e551 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Thu, 11 Jun 2026 18:00:53 +0000 Subject: [PATCH 56/71] fuchsia: Support AddressSanitizer on riscv64gc-unknown-fuchsia --- .../rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index 212704832e55d..2f6b72cdf9722 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { base.llvm_abiname = "lp64d".into(); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::SHADOWCALLSTACK; base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; base.supports_xray = true; From cc89e06a7eeb61a14a68492512260d473872524f Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Mon, 8 Jun 2026 13:18:19 -0400 Subject: [PATCH 57/71] Disable retagging for variadic arguments. Co-authored-by: Ralf Jung --- .../rustc_const_eval/src/interpret/call.rs | 8 +++++-- .../tests/pass/both_borrows/c_variadics.rs | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/tools/miri/tests/pass/both_borrows/c_variadics.rs diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 40ba31d37f2f0..85da687d8af37 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -502,8 +502,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let mplace = ecx.force_allocation(&place)?; // Consume the remaining arguments by putting them into the variable argument - // list. - let varargs = ecx.allocate_varargs(&mut caller_args, &mut callee_args_abis)?; + // list. We disable retagging to avoid creating protected tags. Protection should + // only use callee-side information, and the varargs have no static callee-side type. + let varargs = M::with_retag_mode(ecx, RetagMode::None, |ecx| { + ecx.allocate_varargs(&mut caller_args, &mut callee_args_abis) + })?; + // When the frame is dropped, these variable arguments are deallocated. ecx.frame_mut().va_list = varargs.clone(); let key = ecx.va_list_ptr(varargs.into()); diff --git a/src/tools/miri/tests/pass/both_borrows/c_variadics.rs b/src/tools/miri/tests/pass/both_borrows/c_variadics.rs new file mode 100644 index 0000000000000..c3ec5de350bad --- /dev/null +++ b/src/tools/miri/tests/pass/both_borrows/c_variadics.rs @@ -0,0 +1,21 @@ +//@revisions: stack tree tree_implicit_writes +//@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(c_variadic)] + +fn main() { + unsafe extern "C" fn write_with_first_arg( + ptr_to_val: *mut i32, + _hidden_mut_ref_to_val: ... + ) { + // Retagging needs to be disabled for arguments + // within the VaList. Otherwise, this write access + // will be undefined behavior. + unsafe { *ptr_to_val = 32; } + } + + let mut val: i32 = 0; + unsafe { + write_with_first_arg(&raw mut val, &val); + } +} From 390c6a4bda4dd3b26adeb922aaeb45fd18116cef Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Tue, 9 Jun 2026 15:05:26 -0700 Subject: [PATCH 58/71] use empty assumptions not no assumptions in solver relating --- compiler/rustc_infer/src/infer/context.rs | 16 ++++++++++++- .../src/solve/eval_ctxt/mod.rs | 6 ++--- compiler/rustc_type_ir/src/infer_ctxt.rs | 11 ++++++++- .../rustc_type_ir/src/region_constraint.rs | 10 +++----- .../src/relate/solver_relating.rs | 8 +++---- .../type_relation_binders_inside_solver-1.rs | 23 +++++++++++++++++++ .../type_relation_binders_inside_solver-2.rs | 15 ++++++++++++ .../type_relation_binders_inside_solver-3.rs | 21 +++++++++++++++++ 8 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-1.rs create mode 100644 tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-2.rs create mode 100644 tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-3.rs diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index fc0c4bb0d5f5e..c23bc4adf914c 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -209,7 +209,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { ) } - fn enter_forall>, U>( + fn enter_forall_without_assumptions>, U>( &self, value: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U, @@ -217,6 +217,20 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.enter_forall(value, f) } + fn enter_forall_with_empty_assumptions>, U>( + &self, + value: ty::Binder<'tcx, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, |value| { + let u = self.universe(); + self.placeholder_assumptions_for_next_solver + .borrow_mut() + .insert(u, Some(rustc_type_ir::region_constraint::Assumptions::empty())); + f(value) + }) + } + fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { self.inner.borrow_mut().type_variables().equate(a, b); } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 23fcfc2788656..f6040081a52db 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1331,8 +1331,8 @@ where self.delegate.instantiate_binder_with_infer(value) } - /// `enter_forall`, but takes `&mut self` and passes it back through the - /// callback since it can't be aliased during the call. + /// `enter_forall_with_assumptions`, but takes `&mut self` and passes it back through + /// the callback since it can't be aliased during the call. /// /// The `param_env` is used to *compute* the assumptions of the binder, not *as* the /// assumptions associated with the binder. @@ -1344,7 +1344,7 @@ where param_env: I::ParamEnv, f: impl FnOnce(&mut Self, T) -> U, ) -> U { - self.delegate.enter_forall(value, |value| { + self.delegate.enter_forall_without_assumptions(value, |value| { let u = self.delegate.universe(); let assumptions = if self.cx().assumptions_on_binders() { self.region_assumptions_for_placeholders_in_universe(value.clone(), u, param_env) diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index ea594bb217315..65bb84d7a8b08 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -403,7 +403,16 @@ pub trait InferCtxtLike: Sized { value: ty::Binder, ) -> T; - fn enter_forall, U>( + fn enter_forall_without_assumptions, U>( + &self, + value: ty::Binder, + f: impl FnOnce(T) -> U, + ) -> U; + + /// FIXME(-Zassumptions-on-binders): Any usage of this method is likely wrong + /// and should be replaced in the long term by actually taking assumptions into + /// account. + fn enter_forall_with_empty_assumptions, U>( &self, value: ty::Binder, f: impl FnOnce(T) -> U, diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 43b1535d16809..d523a366d1bf0 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -1096,11 +1096,7 @@ fn alias_outlives_candidates_from_assumptions let prev_universe = infcx.universe(); - // FIXME(-Zassumptions-on-binders): Handle the assumptions on this binder - infcx.enter_forall(bound_outlives, |(alias, r)| { - let u = infcx.universe(); - infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); - + infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| { for bound_type_outlives in assumptions.type_outlives.iter() { let OutlivesPredicate(alias2, r2) = infcx.instantiate_binder_with_infer(*bound_type_outlives); @@ -1187,14 +1183,14 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation where T: Relate, { - self.infcx.enter_forall(a, |a| { + self.infcx.enter_forall_with_empty_assumptions(a, |a| { let u = self.infcx.universe(); self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); let b = self.infcx.instantiate_binder_with_infer(b); self.relate(a, b) })?; - self.infcx.enter_forall(b, |b| { + self.infcx.enter_forall_with_empty_assumptions(b, |b| { let u = self.infcx.universe(); self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); let a = self.infcx.instantiate_binder_with_infer(a); diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index a643d22c17643..96e30f06473cf 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -311,13 +311,13 @@ where // // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html ty::Covariant => { - self.infcx.enter_forall(b, |b| { + self.infcx.enter_forall_with_empty_assumptions(b, |b| { let a = self.infcx.instantiate_binder_with_infer(a); self.relate(a, b) })?; } ty::Contravariant => { - self.infcx.enter_forall(a, |a| { + self.infcx.enter_forall_with_empty_assumptions(a, |a| { let b = self.infcx.instantiate_binder_with_infer(b); self.relate(a, b) })?; @@ -334,13 +334,13 @@ where // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`. // Check if `exists<..> A == for<..> B` ty::Invariant => { - self.infcx.enter_forall(b, |b| { + self.infcx.enter_forall_with_empty_assumptions(b, |b| { let a = self.infcx.instantiate_binder_with_infer(a); self.relate(a, b) })?; // Check if `exists<..> B == for<..> A`. - self.infcx.enter_forall(a, |a| { + self.infcx.enter_forall_with_empty_assumptions(a, |a| { let b = self.infcx.instantiate_binder_with_infer(b); self.relate(a, b) })?; diff --git a/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-1.rs b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-1.rs new file mode 100644 index 0000000000000..4d7427ab4436e --- /dev/null +++ b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-1.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ compile-flags: -Znext-solver -Zassumptions-on-binders + +#![crate_type = "lib"] + +// When entering binders if we don't insert assumptions corresponding to the +// binder then we'll ICE when later trying to eagerly handle placeholders. This +// test checks that type relations involving higher ranked types insert assumptions +// for the binders of the higher ranked types. +// +// This is specifically checking that type relations used from *inside* the trait +// solver do this. In this case the type relation occurs as part of an `AliasRelate` +// involving the rigid alias: `::Assoc fn('b ()) -> &'?0 ()>`. + +trait Trait { type Assoc; } + +fn foo(_: U) -> ::Assoc:: { loop {} } + +fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} } + +fn bar() { + foo::(mk()); +} diff --git a/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-2.rs b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-2.rs new file mode 100644 index 0000000000000..e27339f233015 --- /dev/null +++ b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-2.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ compile-flags: -Znext-solver -Zassumptions-on-binders + +#![crate_type = "lib"] + +// Slight derivative of `type_relation_binders_inside_solver-1.rs` except this time the +// rigid alias is the opaque type `(impl Sized) fn(&'b ()) -> &'?0 ()>` instead. + +fn foo(_: T) -> impl Sized + use {} + +fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} } + +fn bar() { + foo(mk()); +} diff --git a/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-3.rs b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-3.rs new file mode 100644 index 0000000000000..4d223152866d3 --- /dev/null +++ b/tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-3.rs @@ -0,0 +1,21 @@ +//@ check-pass +//@ compile-flags: -Znext-solver -Zassumptions-on-binders + +#![crate_type = "lib"] + +// Same concept as `type_relation_binders_inside_solver-1.rs`, this time the type relation +// occuring when checking `for<'b> fn(&'b ()): Trait` holds and we have to equate the two +// higher ranked function pointer types. + +trait Trait { } + +fn req_trait(_: T) { } + +fn mk() -> for<'b> fn(&'b ()) { loop {} } + +fn ice() +where + (for<'b> fn(&'b ())): Trait +{ + req_trait(mk()); +} From f8aff87d8c6679e3d7bc9e3c2b4e35c4dc27826b Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 12 Jun 2026 06:34:58 +0000 Subject: [PATCH 59/71] revert let pattern check --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 --- tests/ui/wf/let-pat-inferred-non-wf.rs | 58 ----------------- tests/ui/wf/let-pat-inferred-non-wf.stderr | 62 ------------------- 3 files changed, 130 deletions(-) delete mode 100644 tests/ui/wf/let-pat-inferred-non-wf.rs delete mode 100644 tests/ui/wf/let-pat-inferred-non-wf.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 0553276b1053b..f3bf57ab4cd34 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -903,16 +903,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type check the pattern. Override if necessary to avoid knock-on errors. self.check_pat_top(decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin)); - if decl.ty.is_none() - && decl.init.is_none() - && !matches!(decl.pat.kind, hir::PatKind::Binding(.., None) | hir::PatKind::Wild) - { - self.register_wf_obligation( - decl_ty.into(), - decl.pat.span, - ObligationCauseCode::WellFormed(None), - ); - } let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); diff --git a/tests/ui/wf/let-pat-inferred-non-wf.rs b/tests/ui/wf/let-pat-inferred-non-wf.rs deleted file mode 100644 index ab032161ebb51..0000000000000 --- a/tests/ui/wf/let-pat-inferred-non-wf.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Regression test for https://github.com/rust-lang/rust/issues/150040 -// When a `let PAT;` has no explicit type, later assignments can infer a non-well-formed -// pattern type such as `[str; 2]` or `(str, i32)`. We must reject those array and tuple -// patterns instead of accepting the invalid type or causing ICE. - -#![allow(unused)] - -struct S(T); - -fn should_fail_1() { - let ref y @ [ref x, _]; //~ ERROR E0277 - x = ""; -} - -fn should_fail_2() { - let [ref x]; //~ ERROR E0277 - x = ""; -} - -fn should_fail_3() { - let [[ref x], [_, y @ ..]]; //~ ERROR E0277 - x = ""; - y = []; -} - -fn should_fail_4() { - let [(ref a, b), x]; //~ ERROR E0277 - a = ""; - b = 5; -} - -fn should_fail_5() { - let (ref a, b); //~ ERROR E0277 - a = ""; - b = 5; -} - -fn should_fail_6() { - let [S(ref x)]; //~ ERROR E0277 - x = ""; -} - -fn should_pass_1() { - let ref x; - x = ""; -} - -fn should_pass_2() { - let ref y @ (ref x,); - x = ""; -} - -fn should_pass_3() { - let S(ref x); - x = ""; -} - -fn main() {} diff --git a/tests/ui/wf/let-pat-inferred-non-wf.stderr b/tests/ui/wf/let-pat-inferred-non-wf.stderr deleted file mode 100644 index 1c524a22b432b..0000000000000 --- a/tests/ui/wf/let-pat-inferred-non-wf.stderr +++ /dev/null @@ -1,62 +0,0 @@ -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:11:9 - | -LL | let ref y @ [ref x, _]; - | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: slice and array elements must have `Sized` type - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:16:9 - | -LL | let [ref x]; - | ^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: slice and array elements must have `Sized` type - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:21:9 - | -LL | let [[ref x], [_, y @ ..]]; - | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: slice and array elements must have `Sized` type - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:27:9 - | -LL | let [(ref a, b), x]; - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: only the last element of a tuple may have a dynamically sized type - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:33:9 - | -LL | let (ref a, b); - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: only the last element of a tuple may have a dynamically sized type - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/let-pat-inferred-non-wf.rs:39:9 - | -LL | let [S(ref x)]; - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: within `S`, the trait `Sized` is not implemented for `str` -note: required because it appears within the type `S` - --> $DIR/let-pat-inferred-non-wf.rs:8:8 - | -LL | struct S(T); - | ^ - = note: slice and array elements must have `Sized` type - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0277`. From e04bcaf3d24b4dc20e4b947b240a4323961cc8f5 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Fri, 12 Jun 2026 10:09:02 +0300 Subject: [PATCH 60/71] rustdoc: Some more lazy formatting Mostly replaces a few `format!` calls with `format_args!` --- src/librustdoc/html/render/mod.rs | 14 +++--- src/librustdoc/html/render/print_item.rs | 50 ++++++++++------------ src/librustdoc/html/render/write_shared.rs | 2 +- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 5558c36f1d43d..9a08e76604df2 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2955,7 +2955,7 @@ fn render_call_locations( fn render_attributes_in_code( w: &mut impl fmt::Write, item: &clean::Item, - prefix: &str, + prefix: impl fmt::Display, cx: &Context<'_>, ) -> fmt::Result { render_attributes_in_code_with_options(w, item, prefix, cx, true, "") @@ -2964,14 +2964,14 @@ fn render_attributes_in_code( pub(super) fn render_attributes_in_code_with_options( w: &mut impl fmt::Write, item: &clean::Item, - prefix: &str, + prefix: impl fmt::Display, cx: &Context<'_>, render_doc_hidden: bool, open_tag: &str, ) -> fmt::Result { w.write_str(open_tag)?; if render_doc_hidden && item.is_doc_hidden() { - render_code_attribute(prefix, "#[doc(hidden)]", w)?; + render_code_attribute(&prefix, "#[doc(hidden)]", w)?; } for attr in &item.attrs.other_attrs { let hir::Attribute::Parsed(kind) = attr else { continue }; @@ -2986,7 +2986,7 @@ pub(super) fn render_attributes_in_code_with_options( AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), _ => continue, }; - render_code_attribute(prefix, attr.as_ref(), w)?; + render_code_attribute(&prefix, attr.as_ref(), w)?; } if let Some(def_id) = item.def_id() @@ -3008,7 +3008,11 @@ fn render_repr_attribute_in_code( Ok(()) } -fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result { +fn render_code_attribute( + prefix: impl fmt::Display, + attr: impl fmt::Display, + w: &mut impl fmt::Write, +) -> fmt::Result { write!(w, "
{prefix}{attr}
") } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 7ade72429cb4d..7c1cc4ba7f9d9 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Display, Write as _}; use std::iter; @@ -395,25 +396,26 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i let (stab_tags, deprecation) = match import.source.did { Some(import_def_id) => { let stab_tags = - print_extra_info_tags(tcx, myitem, item, Some(import_def_id)) - .to_string(); + print_extra_info_tags(tcx, myitem, item, Some(import_def_id)); let deprecation = tcx .lookup_deprecation(import_def_id) .is_some_and(|deprecation| deprecation.is_in_effect()); - (stab_tags, deprecation) + (Some(stab_tags), deprecation) } - None => (String::new(), item.is_deprecated(tcx)), + None => (None, item.is_deprecated(tcx)), }; let visibility_and_hidden = visibility_and_hidden(myitem); let id = match import.kind { - clean::ImportKind::Simple(s) => { - format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) - } - clean::ImportKind::Glob => String::new(), + clean::ImportKind::Simple(s) => Some(format_args!( + " id=\"{}\"", + cx.derive_id(format!("reexport.{s}")) + )), + clean::ImportKind::Glob => None, }; write!( w, "", + id = id.maybe_display(), deprecation_attr = deprecation_class_attr(deprecation) )?; write!( @@ -423,6 +425,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i vis = visibility_print_with_space(myitem, cx), imp = print_import(import, cx), visibility_and_hidden = visibility_and_hidden, + stab_tags = stab_tags.maybe_display(), )?; } _ => { @@ -512,18 +515,14 @@ fn print_extra_info_tags( write!(f, "{}", tag_html("unstable", "", "Experimental"))?; } + debug!(name = ?item.name, cfg = ?item.cfg, parent_cfg = ?parent.cfg, "Portability"); + let cfg = match (&item.cfg, parent.cfg.as_ref()) { - (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), - (cfg, _) => cfg.as_deref().cloned(), + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg).map(Cow::Owned), + (cfg, _) => cfg.as_deref().map(Cow::Borrowed), }; - debug!( - "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}", - name = item.name, - cfg = item.cfg, - parent_cfg = parent.cfg - ); - if let Some(ref cfg) = cfg { + if let Some(cfg) = cfg { write!( f, "{}", @@ -976,7 +975,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: "Dyn Compatibility", "dyn-compatibility", None, - format!( + format_args!( "

This trait {} \ dyn compatible.

\

In older versions of Rust, dyn compatibility was called \"object safety\".

", @@ -1775,10 +1774,10 @@ fn item_variants( w, "{}", write_section_heading( - &format!("Variants{}", document_non_exhaustive_header(it)), + format_args!("Variants{}", document_non_exhaustive_header(it)), "variants", Some("variants"), - format!("{}
", document_non_exhaustive(it)), + format_args!("{}
", document_non_exhaustive(it)), ), )?; @@ -2105,7 +2104,7 @@ fn item_fields( if let None | Some(CtorKind::Fn) = ctor_kind && fields.peek().is_some() { - let title = format!( + let title = format_args!( "{}{}", if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, document_non_exhaustive_header(it), @@ -2113,12 +2112,7 @@ fn item_fields( write!( w, "{}", - write_section_heading( - &title, - "fields", - Some("fields"), - document_non_exhaustive(it) - ) + write_section_heading(title, "fields", Some("fields"), document_non_exhaustive(it)) )?; for (index, (field, ty)) in fields.enumerate() { let field_name = @@ -2554,7 +2548,7 @@ fn render_struct_fields( } for field in fields { if let clean::StructFieldItem(ref ty) = field.kind { - render_attributes_in_code(w, field, &format!("{tab} "), cx)?; + render_attributes_in_code(w, field, format_args!("{tab} "), cx)?; writeln!( w, "{tab} {vis}{name}: {ty},", diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index c366af8778ed6..ad8c6588e521f 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -413,7 +413,7 @@ impl CratesIndexPart { let layout = &cx.shared.layout; let style_files = &cx.shared.style_files; const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this - let content = format!( + let content = format_args!( "
\

List of all crates

\ \ From 25e83a0969d13dac90d1502924753b504e2854b7 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman <127837395+P8L1@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:09:05 +0200 Subject: [PATCH 61/71] Update library/core/src/clone.rs Co-authored-by: Niko Matsakis --- library/core/src/clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index d03ec3b0066ed..898138ed1f36e 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -316,7 +316,7 @@ pub macro Clone($item:item) { /// `String`, `Box`, owned collections, and similar owned values are not /// `Share`, even though they implement [`Clone`], because cloning them creates /// independent owned storage or value ownership. Mutable references (`&mut T`) -/// are not `Share`. +/// are neither `Clone` nor `Share`, because you cannot have two active at once. /// /// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) /// for implementors, but communicates that the resulting value aliases the same From c9ecb9fc017ad06960839525bdb77a82d1415af5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Jun 2026 15:12:10 +0200 Subject: [PATCH 62/71] interpret: avoid computing layout of sized raw pointee --- compiler/rustc_const_eval/src/interpret/validity.rs | 10 +++++++--- .../miri/tests/pass/too-big-type-behind-raw-ptr.rs | 10 ++++++++++ .../layout/issue-unsized-tail-restatic-ice-122488.rs | 2 +- .../issue-unsized-tail-restatic-ice-122488.stderr | 6 +++--- tests/ui/sized/stack-overflow-trait-infer-98842.stderr | 7 ++----- 5 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 src/tools/miri/tests/pass/too-big-type-behind-raw-ptr.rs diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 249a65e228245..224337e68cfbf 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -925,7 +925,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } interp_ok(true) } - ty::RawPtr(..) => { + ty::RawPtr(pointee, ..) => { let ptr = self.read_immediate(value, ExpectedKind::RawPtr)?; if self.reset_provenance_and_padding { self.reset_pointer_provenance(value, &ptr)?; @@ -933,8 +933,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.add_data_range_place(value); } - let place = self.ecx.imm_ptr_to_mplace(&ptr)?; - if place.layout.is_unsized() { + if !pointee.is_sized(*self.ecx.tcx, self.ecx.typing_env) { + // Raw pointers to unsized types need to have their metadata checked. + // We avoid creating this place for sized types to match codegen: those types + // might actually be invalid (i.e., too big)! + let place = self.ecx.imm_ptr_to_mplace(&ptr)?; + assert!(place.layout.is_unsized()); self.check_wide_ptr_meta(place.meta(), place.layout)?; } interp_ok(true) diff --git a/src/tools/miri/tests/pass/too-big-type-behind-raw-ptr.rs b/src/tools/miri/tests/pass/too-big-type-behind-raw-ptr.rs new file mode 100644 index 0000000000000..b808ed8b979b3 --- /dev/null +++ b/src/tools/miri/tests/pass/too-big-type-behind-raw-ptr.rs @@ -0,0 +1,10 @@ +//! Regression test for : +//! the type behind a raw pointer should never have its layout computed. + +//@compile-flags: -Zmiri-permissive-provenance + +const PTR_BITS_MINUS_1: usize = std::mem::size_of::<*const ()>() * 8 - 1; + +fn main() { + std::hint::black_box(0 as *const [u64; 1 << PTR_BITS_MINUS_1]); +} diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs index 92295704615f2..a9ef59d8df4e3 100644 --- a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs @@ -5,7 +5,7 @@ use std::ops::Deref; struct ArenaSet::Target>(V, U); //~^ ERROR the size for values of type `V` cannot be known at compilation time -const DATA: *const ArenaSet> = std::ptr::null_mut(); +const DATA: &ArenaSet> = unsafe { &* std::ptr::null_mut() }; //~^ ERROR the type `ArenaSet, [u8]>` has an unknown layout pub fn main() {} diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr index ea29320002b8e..0875f5a347e16 100644 --- a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr @@ -23,10 +23,10 @@ LL | struct ArenaSet::Target>(Box, U); | ++++ + error[E0080]: the type `ArenaSet, [u8]>` has an unknown layout - --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:8:1 + --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:8:43 | -LL | const DATA: *const ArenaSet> = std::ptr::null_mut(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here +LL | const DATA: &ArenaSet> = unsafe { &* std::ptr::null_mut() }; + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here error: aborting due to 2 previous errors diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr index b4d21de4fadc8..d0a361b40a704 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr @@ -7,11 +7,8 @@ LL | struct Foo(<&'static Foo as ::core::ops::Deref>::Target); note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... --> $SRC_DIR/core/src/ops/deref.rs:LL:COL = note: ...which again requires computing layout of `Foo`, completing the cycle -note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ +note: cycle used when computing layout of `<&'static Foo as core::ops::deref::Deref>::Target` + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL = note: for more information, see and error: aborting due to 1 previous error From 602df86846ad6edfaae145a1bca7a8108af45dab Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 12 Jun 2026 10:19:58 +0200 Subject: [PATCH 63/71] Bless reborrow UI tests --- tests/ui/reborrow/coerce-shared-field-relations.stderr | 2 +- tests/ui/reborrow/coerce-shared-missing-target-field.stderr | 3 ++- .../ui/reborrow/coerce-shared-mut-ref-field-validation.stderr | 2 +- tests/ui/reborrow/coerce-shared-wrong-generic.stderr | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr index a6ae9ab9524ec..033a29e1e554e 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.stderr +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied - --> $DIR/coerce-shared-field-relations.rs:45:1 + --> $DIR/coerce-shared-field-relations.rs:43:1 | LL | impl<'a, T> CoerceShared> for BadMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut T` diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr index 5d5b8192836b1..15146341bc56c 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr @@ -1,7 +1,8 @@ error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-missing-target-field.rs:19:13 + --> $DIR/coerce-shared-missing-target-field.rs:17:13 | LL | impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr index f716fd7e63c0b..63a96d65e176d 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'a mut u32: CoerceShared<&'a u32>` is not satisfied - --> $DIR/coerce-shared-mut-ref-field-validation.rs:29:1 + --> $DIR/coerce-shared-mut-ref-field-validation.rs:27:1 | LL | impl<'a> CoerceShared> for AliasMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut u32` diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr index 3b07e0f64a212..d0031a7c8a99e 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied - --> $DIR/coerce-shared-wrong-generic.rs:20:1 + --> $DIR/coerce-shared-wrong-generic.rs:18:1 | LL | impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a U>` is not implemented for `&'a mut T` From 23e03d92ad1b0a0cd3f98dc926fb0bbf2103ff9f Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 10 Jun 2026 09:37:12 +0100 Subject: [PATCH 64/71] Extend capabilities of `TypeFoldable_Generic` --- Cargo.lock | 1 + compiler/rustc_type_ir_macros/Cargo.toml | 1 + compiler/rustc_type_ir_macros/src/lib.rs | 111 +++++++++++++++++++---- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdfbb372b14c9..1425d2e9464fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4892,6 +4892,7 @@ dependencies = [ name = "rustc_type_ir_macros" version = "0.0.0" dependencies = [ + "indexmap", "proc-macro2", "quote", "syn", diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml index 910eef5e21f41..41562bd4baeb1 100644 --- a/compiler/rustc_type_ir_macros/Cargo.toml +++ b/compiler/rustc_type_ir_macros/Cargo.toml @@ -11,6 +11,7 @@ nightly = [] [dependencies] # tidy-alphabetical-start +indexmap = "2.4.0" proc-macro2 = "1" quote = "1" syn = { version = "2.0.9", features = ["full", "visit-mut"] } diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index 9d576c3deacee..bafd8d72dc437 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -1,3 +1,4 @@ +use indexmap::IndexSet; use quote::{ToTokens, quote}; use syn::visit_mut::VisitMut; use syn::{Attribute, parse_quote}; @@ -17,11 +18,24 @@ decl_derive!( [GenericTypeVisitable] => customizable_type_visitable_derive ); -struct LiftedTy { +struct TransformedTy { ty: syn::Type, - generic_parameter_bounds: Vec, + generic_parameter_bounds: IndexSet, } +enum TypeParameterPath { + Interner, + GenericParameter(syn::Ident), +} + +enum TypeParameterTransform { + Continue, + Stop, +} + +type TypeParameterVisitor = + fn(TypeParameterPath, &mut syn::TypePath, &mut IndexSet) -> TypeParameterTransform; + fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool { let mut ignored = false; attrs.iter().for_each(|attr| { @@ -91,6 +105,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke s.add_where_predicate(parse_quote! { I: Interner }); s.add_bounds(synstructure::AddBounds::Fields); + let generic_parameters = + s.ast().generics.type_params().map(|ty| ty.ident.clone()).collect::>(); + let mut generic_parameter_bounds = IndexSet::new(); s.bind_with(|_| synstructure::BindStyle::Move); let body_try_fold = s.each_variant(|vi| { let bindings = vi.bindings(); @@ -101,6 +118,12 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { bind.to_token_stream() } else { + for param in + type_foldable_generic_parameters(bind.ast().ty.clone(), &generic_parameters) + { + generic_parameter_bounds.insert(param); + } + quote! { ::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)? } @@ -129,6 +152,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke // to generate code for them. s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity")); s.add_bounds(synstructure::AddBounds::Fields); + for param in generic_parameter_bounds { + s.add_where_predicate(parse_quote! { #param: ::rustc_type_ir::TypeFoldable }); + } s.bound_impl( quote!(::rustc_type_ir::TypeFoldable), quote! { @@ -149,6 +175,19 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke ) } +fn type_foldable_generic_parameters( + ty: syn::Type, + generic_parameters: &[syn::Ident], +) -> IndexSet { + transform_type_parameters(ty, generic_parameters, |path, _, generic_parameter_bounds| { + if let TypeParameterPath::GenericParameter(param) = path { + generic_parameter_bounds.insert(param); + } + TypeParameterTransform::Continue + }) + .generic_parameter_bounds +} + /// `Lift_Generic` is specialised for structs/enums parameterised by an interner /// `I: Interner`. It derives `Lift` by rewriting interner associated types /// from `I::Assoc` to `J::Assoc`. The required associated type lift bounds are @@ -251,40 +290,72 @@ fn is_type_phantom(ty: &syn::Type) -> bool { get_first_path_segment(ty).is_some_and(|segment| segment.ident == "PhantomData") } -fn lift(mut ty: syn::Type, generic_parameters: &[syn::Ident]) -> LiftedTy { - struct ItoJ<'a> { +fn lift(ty: syn::Type, generic_parameters: &[syn::Ident]) -> TransformedTy { + transform_type_parameters(ty, generic_parameters, |path, ty, generic_parameter_bounds| { + match path { + TypeParameterPath::Interner => { + *ty.path.segments.first_mut().unwrap() = parse_quote! { J }; + TypeParameterTransform::Continue + } + TypeParameterPath::GenericParameter(param) => { + generic_parameter_bounds.insert(param.clone()); + *ty = parse_quote! { <#param as ::rustc_type_ir::lift::Lift>::Lifted }; + TypeParameterTransform::Stop + } + } + }) +} + +fn transform_type_parameters( + mut ty: syn::Type, + generic_parameters: &[syn::Ident], + visit: TypeParameterVisitor, +) -> TransformedTy { + struct TypeParameterTransformer<'a> { generic_parameters: &'a [syn::Ident], - generic_parameter_bounds: Vec, + generic_parameter_bounds: IndexSet, + visit: TypeParameterVisitor, } - impl VisitMut for ItoJ<'_> { + impl VisitMut for TypeParameterTransformer<'_> { fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) { - if i.qself.is_none() { + let path = if i.qself.is_none() { let segments_len = i.path.segments.len(); - if let Some(first) = i.path.segments.first_mut() { - // Turn paths from `I` into `J` + i.path.segments.first().and_then(|first| { if first.ident == "I" { - *first = parse_quote! { J }; + Some(TypeParameterPath::Interner) } else if segments_len == 1 && matches!(first.arguments, syn::PathArguments::None) - && self.generic_parameters.iter().any(|param| first.ident == *param) + && self.generic_parameters.contains(&first.ident) { - let ident = first.ident.clone(); - if !self.generic_parameter_bounds.iter().any(|param| *param == ident) { - self.generic_parameter_bounds.push(ident.clone()); - } - - *i = parse_quote! { <#ident as ::rustc_type_ir::lift::Lift>::Lifted }; - return; + Some(TypeParameterPath::GenericParameter(first.ident.clone())) + } else { + None } + }) + } else { + None + }; + + if let Some(path) = path { + if let TypeParameterTransform::Stop = + (self.visit)(path, i, &mut self.generic_parameter_bounds) + { + return; } } + syn::visit_mut::visit_type_path_mut(self, i); } } - let mut visitor = ItoJ { generic_parameters, generic_parameter_bounds: Vec::new() }; + + let mut visitor = TypeParameterTransformer { + generic_parameters, + generic_parameter_bounds: IndexSet::new(), + visit, + }; visitor.visit_type_mut(&mut ty); - LiftedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds } + TransformedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds } } #[cfg(not(feature = "nightly"))] From ea8f0d8194714e8d34fc8a65dc9a85a8cebcbe70 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 10 Jun 2026 17:05:35 +0800 Subject: [PATCH 65/71] Mask CRC intrinsic inputs to match LoongArch hardware semantics LoongArch CRC b/h/w instructions only consume the low 8/16/32 bits of their input operand, ignoring any higher bits. However, Miri forwarded the full value to compute_crc32, which expects all bits above the specified width to be zero and may panic otherwise. Mask off the unused high bits before calling compute_crc32 so the shim matches hardware behavior and correctly handles sign-extended intrinsic arguments. --- src/tools/miri/src/shims/loongarch.rs | 15 ++++++++--- .../loongarch/intrinsics-loongarch64-crc.rs | 25 ++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/src/shims/loongarch.rs b/src/tools/miri/src/shims/loongarch.rs index e5a57fbcaebfb..8642a5ce1a47b 100644 --- a/src/tools/miri/src/shims/loongarch.rs +++ b/src/tools/miri/src/shims/loongarch.rs @@ -1,4 +1,4 @@ -use rustc_abi::CanonAbi; +use rustc_abi::{CanonAbi, Size}; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; @@ -55,10 +55,17 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // b/h/w variants and i64 for the d variant, per the LLVM intrinsic // definitions. // https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/IntrinsicsLoongArch.td - // If the higher bits are non-zero, `compute_crc32` will panic. We should probably - // raise a proper error instead, but outside stdarch nobody can trigger this anyway. + // LoongArch CRC b/h/w instructions ignore any bits above `bit_size`. + // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#crc-check-instructions + // Miri's `compute_crc32` requires all higher bits to be zero and may + // panic otherwise, so we explicitly mask them off here to reproduce the + // hardware behavior. let crc = crc.to_u32()?; - let data = if bit_size == 64 { data.to_u64()? } else { u64::from(data.to_u32()?) }; + let data = if bit_size == 64 { + data.to_u64()? + } else { + Size::from_bits(bit_size).truncate(data.to_u32()?.into()).try_into().unwrap() + }; let result = compute_crc32(crc, data, bit_size, polynomial); this.write_scalar(Scalar::from_u32(result), dest)?; diff --git a/src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs b/src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs index cc96653991234..0e208b42215c0 100644 --- a/src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs +++ b/src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs @@ -1,9 +1,20 @@ // We're testing loongarch64-specific intrinsics //@only-target: loongarch64 -#![feature(stdarch_loongarch)] +#![feature(abi_unadjusted, link_llvm_intrinsics, stdarch_loongarch)] use std::arch::loongarch64::*; +unsafe extern "unadjusted" { + #[link_name = "llvm.loongarch.crc.w.b.w"] + fn _crc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crc.w.h.w"] + fn _crc_w_h_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.b.w"] + fn _crcc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.h.w"] + fn _crcc_w_h_w(a: i32, b: i32) -> i32; +} + fn main() { test_crc_ieee(); test_crc_castagnoli(); @@ -12,13 +23,19 @@ fn main() { fn test_crc_ieee() { // crc.w.b.w: 8-bit input assert_eq!(crc_w_b_w(0x01, 0x00000000), 0x77073096); + assert_eq!(unsafe { _crc_w_b_w(0x1_01, 0x00000000) }, 0x77073096); // higher bits in the first argument are ignored assert_eq!(crc_w_b_w(0x61, 0xffffffff_u32 as i32), 0x174841bc); + assert_eq!(unsafe { _crc_w_b_w(0x2_61, 0xffffffff_u32 as i32) }, 0x174841bc); assert_eq!(crc_w_b_w(0x2a, 0x2aa1e72b), 0x772d9171); + assert_eq!(unsafe { _crc_w_b_w(0x3_2a, 0x2aa1e72b) }, 0x772d9171); // crc.w.h.w: 16-bit input assert_eq!(crc_w_h_w(0x0001, 0x00000000), 0x191b3141); + assert_eq!(unsafe { _crc_w_h_w(0x1_0001, 0x00000000) }, 0x191b3141); assert_eq!(crc_w_h_w(0x1234, 0xffffffff_u32 as i32), 0xf6b56fbf_u32 as i32); + assert_eq!(unsafe { _crc_w_h_w(0x2_1234, 0xffffffff_u32 as i32) }, 0xf6b56fbf_u32 as i32); assert_eq!(crc_w_h_w(0x022b, 0x8ecec3b5_u32 as i32), 0x03a1db7c); + assert_eq!(unsafe { _crc_w_h_w(0x3_022b, 0x8ecec3b5_u32 as i32) }, 0x03a1db7c); // crc.w.w.w: 32-bit input assert_eq!(crc_w_w_w(0x00000001, 0x00000000), 0xb8bc6765_u32 as i32); @@ -34,13 +51,19 @@ fn test_crc_ieee() { fn test_crc_castagnoli() { // crcc.w.b.w: 8-bit input assert_eq!(crcc_w_b_w(0x01, 0x00000000), 0xf26b8303_u32 as i32); + assert_eq!(unsafe { _crcc_w_b_w(0x1_01, 0x00000000) }, 0xf26b8303_u32 as i32); assert_eq!(crcc_w_b_w(0x61, 0xffffffff_u32 as i32), 0x3e2fbccf); + assert_eq!(unsafe { _crcc_w_b_w(0x2_61, 0xffffffff_u32 as i32) }, 0x3e2fbccf); assert_eq!(crcc_w_b_w(0x2a, 0x2aa1e72b), 0xf24122e4_u32 as i32); + assert_eq!(unsafe { _crcc_w_b_w(0x3_2a, 0x2aa1e72b) }, 0xf24122e4_u32 as i32); // crcc.w.h.w: 16-bit input assert_eq!(crcc_w_h_w(0x0001, 0x00000000), 0x13a29877); + assert_eq!(unsafe { _crcc_w_h_w(0x1_0001, 0x00000000) }, 0x13a29877); assert_eq!(crcc_w_h_w(0x1234, 0xffffffff_u32 as i32), 0xf13f4cea_u32 as i32); + assert_eq!(unsafe { _crcc_w_h_w(0x2_1234, 0xffffffff_u32 as i32) }, 0xf13f4cea_u32 as i32); assert_eq!(crcc_w_h_w(0x022b, 0x8ecec3b5_u32 as i32), 0x013bb2fb); + assert_eq!(unsafe { _crcc_w_h_w(0x3_022b, 0x8ecec3b5_u32 as i32) }, 0x013bb2fb); // crcc.w.w.w: 32-bit input assert_eq!(crcc_w_w_w(0x00000001, 0x00000000), 0xdd45aab8_u32 as i32); From 42d7bf1edf0ece82edcc55c108dd60559f4bd9c1 Mon Sep 17 00:00:00 2001 From: Dominik Schwaiger Date: Fri, 12 Jun 2026 08:55:09 +0000 Subject: [PATCH 66/71] add #[rustc_no_writable] to slice::get_unchecked_mut * add #[rustc_no_writable] to slice::get_unchecked_mut * add #[rustc_no_writable] to slice::get_mut * add unchecked_mut miri test --- library/core/src/slice/mod.rs | 2 ++ .../implicit_writes/unchecked_mut.rs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/tools/miri/tests/pass/tree_borrows/implicit_writes/unchecked_mut.rs diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a838ba009b484..41c49b11e0b03 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -596,6 +596,7 @@ impl [T] { #[inline] #[must_use] #[rustc_const_unstable(feature = "const_index", issue = "143775")] + #[rustc_no_writable] pub const fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where I: [const] SliceIndex, @@ -681,6 +682,7 @@ impl [T] { #[must_use] #[track_caller] #[rustc_const_unstable(feature = "const_index", issue = "143775")] + #[rustc_no_writable] pub const unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where I: [const] SliceIndex, diff --git a/src/tools/miri/tests/pass/tree_borrows/implicit_writes/unchecked_mut.rs b/src/tools/miri/tests/pass/tree_borrows/implicit_writes/unchecked_mut.rs new file mode 100644 index 0000000000000..e2785183ff599 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/implicit_writes/unchecked_mut.rs @@ -0,0 +1,28 @@ +// This test reproduces the pattern used by `BorrowedCursor::as_mut`, which appears in `Socket::recv_with_flags` and `std::fs::read`. +// Many crates depend on similar patterns. Before https://github.com/rust-lang/rust/pull/157202 this failed under Tree +// Borrows with Implicit Writes. With the attribute `#[rustc_no_writable]` added to +// `slice::get_unchecked_mut`, both this test and the affected crates work. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +struct BorrowedBuf<'a> { + buf: &'a mut [u8], +} + +impl<'a> BorrowedBuf<'a> { + fn capacity(&self) -> usize { + self.buf.len() + } + + unsafe fn as_mut(&mut self) -> &mut [u8] { + unsafe { self.buf.get_unchecked_mut(..) } + } +} + +fn main() { + let mut arr = [0u8; 4]; + let mut buf = BorrowedBuf { buf: &mut arr }; + + let ptr = unsafe { buf.as_mut() }.as_mut_ptr(); + let _ = buf.capacity(); + unsafe { ptr.write(42); } +} From c15d9aba83d53476af02bacb88c9c77f57ce1066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 12 Jun 2026 10:30:05 +0200 Subject: [PATCH 67/71] Prevent approving PRs that wait for Crater or formal decisions --- rust-bors.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust-bors.toml b/rust-bors.toml index 0efbb5e8fc61c..c9fd2e568aca3 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -26,6 +26,15 @@ labels_blocking_approval = [ "S-waiting-on-t-clippy", # PR manually set to blocked "S-blocked", + # Needs a Crater run before being approved + "needs-crater", + # Needs a formal decision to be made + "needs-rfc", + "needs-fcp", + "needs-acp", + "needs-mcp", + # A PR in the Rust reference must be done first + "needs-reference-pr" ] # If CI runs quicker than this duration, consider it to be a failure From 9dcf73ac448bb45b735f49ab5d928c16e4f31d19 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Jun 2026 12:40:08 +0200 Subject: [PATCH 68/71] Rename `rustc_hir_monomorphize/src/errors.rs` into `rustc_hir_monomorphize/src/diagnostics.rs` --- compiler/rustc_monomorphize/src/collector.rs | 4 ++-- .../src/{errors.rs => diagnostics.rs} | 0 .../rustc_monomorphize/src/graph_checks/statics.rs | 4 ++-- compiler/rustc_monomorphize/src/lib.rs | 2 +- .../rustc_monomorphize/src/mono_checks/abi_check.rs | 12 ++++++------ .../rustc_monomorphize/src/mono_checks/move_check.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) rename compiler/rustc_monomorphize/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 8c5a8a625ec99..0421edc543151 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -235,7 +235,7 @@ use rustc_session::config::{DebugInfo, EntryFnType}; use rustc_span::{DUMMY_SP, Span, Spanned, dummy_spanned, respan}; use tracing::{debug, instrument, trace}; -use crate::errors::{ +use crate::diagnostics::{ self, EncounteredErrorWhileInstantiating, EncounteredErrorWhileInstantiatingGlobalAsm, NoOptimizedMir, RecursionLimit, }; @@ -1702,7 +1702,7 @@ impl<'v> RootCollector<'_, 'v> { } let Some(start_def_id) = self.tcx.lang_items().start_fn() else { - self.tcx.dcx().emit_fatal(errors::StartNotFound); + self.tcx.dcx().emit_fatal(diagnostics::StartNotFound); }; let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output(); diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/diagnostics.rs similarity index 100% rename from compiler/rustc_monomorphize/src/errors.rs rename to compiler/rustc_monomorphize/src/diagnostics.rs diff --git a/compiler/rustc_monomorphize/src/graph_checks/statics.rs b/compiler/rustc_monomorphize/src/graph_checks/statics.rs index 16642b9960126..4a6416843fded 100644 --- a/compiler/rustc_monomorphize/src/graph_checks/statics.rs +++ b/compiler/rustc_monomorphize/src/graph_checks/statics.rs @@ -8,7 +8,7 @@ use rustc_middle::mono::MonoItem; use rustc_middle::ty::TyCtxt; use crate::collector::UsageMap; -use crate::errors; +use crate::diagnostics; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct StaticNodeIdx(usize); @@ -105,7 +105,7 @@ pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>( let head_def = statics[nodes[0].index()]; let head_span = tcx.def_span(head_def); - tcx.dcx().emit_err(errors::StaticInitializerCyclic { + tcx.dcx().emit_err(diagnostics::StaticInitializerCyclic { span: head_span, labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(), head: &tcx.def_path_str(head_def), diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index ae97bf830d8c0..0b5a3d1f267da 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -13,7 +13,7 @@ use rustc_middle::{bug, traits}; use rustc_span::ErrorGuaranteed; mod collector; -mod errors; +mod diagnostics; mod graph_checks; mod mono_checks; mod partitioning; diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 812979d13b26f..8b79eaae28753 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -8,7 +8,7 @@ use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_target::callconv::{FnAbi, PassMode}; -use crate::errors; +use crate::diagnostics; /// Are vector registers used? enum UsesVectorRegisters { @@ -71,7 +71,7 @@ fn do_check_simd_vector_abi<'tcx>( Some((_, feature)) => feature, None => { let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { + tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedVectorType { span, ty: arg_abi.layout.ty, is_call, @@ -81,7 +81,7 @@ fn do_check_simd_vector_abi<'tcx>( }; if !feature.is_empty() && !have_feature(Symbol::intern(feature)) { let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { + tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType { span, required_feature: feature, ty: arg_abi.layout.ty, @@ -98,7 +98,7 @@ fn do_check_simd_vector_abi<'tcx>( }; if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) { let (span, _) = loc(); - tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { + tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType { span, required_feature, ty: arg_abi.layout.ty, @@ -115,7 +115,7 @@ fn do_check_simd_vector_abi<'tcx>( // The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed. if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) { let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiRequiredTargetFeature { + tcx.dcx().emit_err(diagnostics::AbiRequiredTargetFeature { span, required_feature: "sse2", abi: "vectorcall", @@ -142,7 +142,7 @@ fn do_check_unsized_params<'tcx>( for arg_abi in fn_abi.args.iter() { if !arg_abi.layout.layout.is_sized() { let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter { + tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedUnsizedParameter { span, ty: arg_abi.layout.ty, is_call, diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index af03e2a0d2d66..f0fe4c9056187 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -9,7 +9,7 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_span::{Span, Spanned, sym}; use tracing::{debug, trace}; -use crate::errors::LargeAssignmentsLint; +use crate::diagnostics::LargeAssignmentsLint; struct MoveCheckVisitor<'tcx> { tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 6058cb892dc14..aee7153419883 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -124,7 +124,7 @@ use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::collector::{self, MonoItemCollectionStrategy, UsageMap}; -use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined}; +use crate::diagnostics::{CouldntDumpMonoStats, SymbolAlreadyDefined}; use crate::graph_checks::target_specific_checks; struct PartitioningCx<'a, 'tcx> { From d0b019e0abd4303490a13c2513a2469fad4c8c25 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Jun 2026 12:44:53 +0200 Subject: [PATCH 69/71] Rename `rustc_incremental/src/errors.rs` into `rustc_incremental/src/diagnostics.rs` --- .../rustc_incremental/src/assert_dep_graph.rs | 17 ++++++------ .../src/{errors.rs => diagnostics.rs} | 0 compiler/rustc_incremental/src/lib.rs | 2 +- .../rustc_incremental/src/persist/clean.rs | 22 ++++++++-------- .../src/persist/file_format.rs | 8 +++--- compiler/rustc_incremental/src/persist/fs.rs | 26 ++++++++++--------- .../rustc_incremental/src/persist/load.rs | 19 +++++++------- .../rustc_incremental/src/persist/save.rs | 4 +-- .../src/persist/work_product.rs | 6 ++--- 9 files changed, 54 insertions(+), 50 deletions(-) rename compiler/rustc_incremental/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index b3f9a137817a7..8a13cb3e9a96d 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -52,7 +52,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol, sym}; use tracing::debug; -use crate::errors; +use crate::diagnostics; #[allow(missing_docs)] pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) { @@ -128,7 +128,7 @@ impl<'tcx> IfThisChanged<'tcx> { Err(()) => self .tcx .dcx() - .emit_fatal(errors::UnrecognizedDepNode { span, name: n }), + .emit_fatal(diagnostics::UnrecognizedDepNode { span, name: n }), } } }; @@ -139,9 +139,10 @@ impl<'tcx> IfThisChanged<'tcx> { let Ok(dep_node) = DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) else { - self.tcx - .dcx() - .emit_fatal(errors::UnrecognizedDepNode { span: n.span, name: n.name }); + self.tcx.dcx().emit_fatal(diagnostics::UnrecognizedDepNode { + span: n.span, + name: n.name, + }); }; self.then_this_would_need.push((n.span, n.name, hir_id, dep_node)); } @@ -186,7 +187,7 @@ fn check_paths<'tcx>( ) { if if_this_changed.is_empty() { for &(target_span, _, _, _) in then_this_would_need { - tcx.dcx().emit_err(errors::MissingIfThisChanged { span: target_span }); + tcx.dcx().emit_err(diagnostics::MissingIfThisChanged { span: target_span }); } return; } @@ -195,13 +196,13 @@ fn check_paths<'tcx>( let dependents = query.transitive_predecessors(source_dep_node); for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { if !dependents.contains(&target_dep_node) { - tcx.dcx().emit_err(errors::NoPath { + tcx.dcx().emit_err(diagnostics::NoPath { span: target_span, source: tcx.def_path_str(source_def_id), target: *target_pass, }); } else { - tcx.dcx().emit_err(errors::Ok { span: target_span }); + tcx.dcx().emit_err(diagnostics::Ok { span: target_span }); } } } diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/diagnostics.rs similarity index 100% rename from compiler/rustc_incremental/src/errors.rs rename to compiler/rustc_incremental/src/diagnostics.rs diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index ebaed7bd16346..0f5d9c5389667 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -6,7 +6,7 @@ // tidy-alphabetical-end mod assert_dep_graph; -mod errors; +mod diagnostics; mod persist; pub use persist::{ diff --git a/compiler/rustc_incremental/src/persist/clean.rs b/compiler/rustc_incremental/src/persist/clean.rs index 800805551af63..a8eb1ff3d5a42 100644 --- a/compiler/rustc_incremental/src/persist/clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -33,7 +33,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol}; use tracing::debug; -use crate::errors; +use crate::diagnostics; // Base and Extra labels to build up the labels @@ -193,7 +193,7 @@ impl<'tcx> CleanVisitor<'tcx> { let loaded_from_disk = self.loaded_from_disk(attr); for e in except.items().into_sorted_stable_ord() { if !auto.remove(e) { - self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); + self.tcx.dcx().emit_fatal(diagnostics::AssertionAuto { span: attr.span, name, e }); } } Assertion { clean: auto, dirty: except, loaded_from_disk } @@ -267,7 +267,7 @@ impl<'tcx> CleanVisitor<'tcx> { // An implementation, eg `impl Trait for Foo { .. }` HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), - _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { + _ => self.tcx.dcx().emit_fatal(diagnostics::UndefinedCleanDirtyItem { span, kind: format!("{:?}", item.kind), }), @@ -286,7 +286,7 @@ impl<'tcx> CleanVisitor<'tcx> { _ => self .tcx .dcx() - .emit_fatal(errors::UndefinedCleanDirty { span, kind: format!("{node:?}") }), + .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: format!("{node:?}") }), }; let labels = Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); @@ -301,13 +301,13 @@ impl<'tcx> CleanVisitor<'tcx> { if out.contains(label_str) { self.tcx .dcx() - .emit_fatal(errors::RepeatedDepNodeLabel { span, label: label_str }); + .emit_fatal(diagnostics::RepeatedDepNodeLabel { span, label: label_str }); } out.insert(label_str.to_string()); } else { self.tcx .dcx() - .emit_fatal(errors::UnrecognizedDepNodeLabel { span, label: label_str }); + .emit_fatal(diagnostics::UnrecognizedDepNodeLabel { span, label: label_str }); } } out @@ -328,7 +328,7 @@ impl<'tcx> CleanVisitor<'tcx> { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .dcx() - .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str }); + .emit_err(diagnostics::NotDirty { span: item_span, dep_node_str: &dep_node_str }); } } @@ -339,7 +339,7 @@ impl<'tcx> CleanVisitor<'tcx> { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .dcx() - .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str }); + .emit_err(diagnostics::NotClean { span: item_span, dep_node_str: &dep_node_str }); } } @@ -369,7 +369,7 @@ impl<'tcx> CleanVisitor<'tcx> { Ok(dep_node) => { if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) { let dep_node_str = self.dep_node_str(&dep_node); - self.tcx.dcx().emit_err(errors::NotLoaded { + self.tcx.dcx().emit_err(diagnostics::NotLoaded { span: item_span, dep_node_str: &dep_node_str, }); @@ -379,7 +379,7 @@ impl<'tcx> CleanVisitor<'tcx> { Err(()) => { let dep_kind = dep_kind_from_label(label); if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) { - self.tcx.dcx().emit_err(errors::NotLoaded { + self.tcx.dcx().emit_err(diagnostics::NotLoaded { span: item_span, dep_node_str: &label, }); @@ -407,7 +407,7 @@ impl<'tcx> FindAllAttrs<'tcx> { fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { if !checked_attrs.contains(&attr.span) { - self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); + self.tcx.dcx().emit_err(diagnostics::UncheckedClean { span: attr.span }); checked_attrs.insert(attr.span); } } diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index eee40fbedf569..853a5c9ba7ab0 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -20,7 +20,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::Session; use tracing::debug; -use crate::errors; +use crate::diagnostics; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; @@ -57,12 +57,12 @@ where debug!("save: remove old file"); } Err(err) if err.kind() == io::ErrorKind::NotFound => (), - Err(err) => sess.dcx().emit_fatal(errors::DeleteOld { name, path: path_buf, err }), + Err(err) => sess.dcx().emit_fatal(diagnostics::DeleteOld { name, path: path_buf, err }), } let mut encoder = match FileEncoder::new(&path_buf) { Ok(encoder) => encoder, - Err(err) => sess.dcx().emit_fatal(errors::CreateNew { name, path: path_buf, err }), + Err(err) => sess.dcx().emit_fatal(diagnostics::CreateNew { name, path: path_buf, err }), }; write_file_header(&mut encoder, sess); @@ -76,7 +76,7 @@ where ); debug!("save: data written to disk successfully"); } - Err((path, err)) => sess.dcx().emit_fatal(errors::WriteNew { name, path, err }), + Err((path, err)) => sess.dcx().emit_fatal(diagnostics::WriteNew { name, path, err }), } } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 9e22935bb646f..80134d5ff2202 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -120,7 +120,7 @@ use rustc_session::{Session, StableCrateId}; use rustc_span::Symbol; use tracing::debug; -use crate::errors; +use crate::diagnostics; #[cfg(test)] mod tests; @@ -233,7 +233,7 @@ pub(crate) fn prepare_session_directory( let crate_dir = match try_canonicalize(&crate_dir) { Ok(v) => v, Err(err) => { - sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err }); + sess.dcx().emit_fatal(diagnostics::CanonicalizePath { path: crate_dir, err }); } }; @@ -276,7 +276,7 @@ pub(crate) fn prepare_session_directory( debug!("successfully copied data from: {}", source_directory.display()); if !allows_links { - sess.dcx().emit_warn(errors::HardLinkFailed { path: &session_dir }); + sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir }); } sess.init_incr_comp_session(session_dir, directory_lock); @@ -291,7 +291,7 @@ pub(crate) fn prepare_session_directory( // Try to remove the session directory we just allocated. We don't // know if there's any garbage in it from the failed copy action. if let Err(err) = std_fs::remove_dir_all(&session_dir) { - sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err }); + sess.dcx().emit_warn(diagnostics::DeletePartial { path: &session_dir, err }); } delete_session_dir_lock_file(sess, &lock_file_path); @@ -325,7 +325,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { ); if let Err(err) = std_fs::remove_dir_all(&*incr_comp_session_dir) { - sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err }); + sess.dcx().emit_warn(diagnostics::DeleteFull { path: &incr_comp_session_dir, err }); } let lock_file_path = lock_file_path(&*incr_comp_session_dir); @@ -364,7 +364,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { } Err(e) => { // Warn about the error. However, no need to abort compilation now. - sess.dcx().emit_note(errors::Finalize { path: &incr_comp_session_dir, err: e }); + sess.dcx().emit_note(diagnostics::Finalize { path: &incr_comp_session_dir, err: e }); debug!("finalize_session_directory() - error, marking as invalid"); // Drop the file lock, so we can garage collect @@ -464,7 +464,9 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { Ok(()) => { debug!("{} directory created successfully", dir_tag); } - Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }), + Err(err) => { + sess.dcx().emit_fatal(diagnostics::CreateIncrCompDir { tag: dir_tag, path, err }) + } } } @@ -483,7 +485,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) Ok(lock) => (lock, lock_file_path), Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - sess.dcx().emit_fatal(errors::CreateLock { + sess.dcx().emit_fatal(diagnostics::CreateLock { lock_err, session_dir, is_unsupported_lock, @@ -495,7 +497,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) { if let Err(err) = safe_remove_file(lock_file_path) { - sess.dcx().emit_warn(errors::DeleteLock { path: lock_file_path, err }); + sess.dcx().emit_warn(diagnostics::DeleteLock { path: lock_file_path, err }); } } @@ -708,7 +710,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { let path = crate_directory.join(directory_name); if let Err(err) = std_fs::remove_dir_all(&path) { - sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err }); + sess.dcx().emit_warn(diagnostics::InvalidGcFailed { path: &path, err }); } } } @@ -840,7 +842,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = std_fs::remove_dir_all(&path) { - sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err }); + sess.dcx().emit_warn(diagnostics::FinalizedGcFailed { path: &path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(&path)); } @@ -858,7 +860,7 @@ fn delete_old(sess: &Session, path: &Path) { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = std_fs::remove_dir_all(path) { - sess.dcx().emit_warn(errors::SessionGcFailed { path, err }); + sess.dcx().emit_warn(diagnostics::SessionGcFailed { path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(path)); } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 6229c66afa75b..c3b9f433417a6 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -18,7 +18,7 @@ use tracing::{debug, warn}; use super::data::*; use super::fs::*; use super::{file_format, work_product}; -use crate::errors; +use crate::diagnostics; use crate::persist::file_format::{OpenFile, OpenFileError}; #[derive(Debug)] @@ -60,7 +60,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult { { // Decode the list of work_products let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else { - sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path }); + sess.dcx().emit_warn(diagnostics::CorruptFile { path: &work_products_path }); return LoadResult::DataOutOfDate; }; let work_products: Vec = @@ -93,7 +93,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult { Err(OpenFileError::IoError { err }) => LoadResult::IoError { path: path.to_owned(), err }, Ok(OpenFile { mmap, start_pos }) => { let Ok(mut decoder) = MemDecoder::new(&mmap, start_pos) else { - sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + sess.dcx().emit_warn(diagnostics::CorruptFile { path: &path }); return LoadResult::DataOutOfDate; }; let prev_commandline_args_hash = Hash64::decode(&mut decoder); @@ -135,7 +135,7 @@ pub fn load_query_result_cache(sess: &Session) -> Option { match file_format::open_incremental_file(sess, &path) { Ok(OpenFile { mmap, start_pos }) => { let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| { - sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + sess.dcx().emit_warn(diagnostics::CorruptFile { path: &path }); OnDiskCache::new_empty() }); Some(cache) @@ -161,12 +161,12 @@ fn maybe_assert_incr_state(sess: &Session, load_result: &LoadResult) { match assertion { IncrementalStateAssertion::Loaded => { if !loaded { - sess.dcx().emit_fatal(errors::AssertLoaded); + sess.dcx().emit_fatal(diagnostics::AssertLoaded); } } IncrementalStateAssertion::NotLoaded => { if loaded { - sess.dcx().emit_fatal(errors::AssertNotLoaded) + sess.dcx().emit_fatal(diagnostics::AssertNotLoaded) } } } @@ -205,12 +205,13 @@ pub fn setup_dep_graph( let (prev_graph, prev_work_products) = match load_result { LoadResult::IoError { path, err } => { - sess.dcx().emit_warn(errors::LoadDepGraph { path, err }); + sess.dcx().emit_warn(diagnostics::LoadDepGraph { path, err }); Default::default() } LoadResult::DataOutOfDate => { if let Err(err) = delete_all_session_dir_contents(sess) { - sess.dcx().emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err }); + sess.dcx() + .emit_err(diagnostics::DeleteIncompatible { path: dep_graph_path(sess), err }); } Default::default() } @@ -223,7 +224,7 @@ pub fn setup_dep_graph( let mut encoder = FileEncoder::new(&path_buf).unwrap_or_else(|err| { // We're in incremental mode but couldn't set up streaming output of the dep graph. // Exit immediately instead of continuing in an inconsistent and untested state. - sess.dcx().emit_fatal(errors::CreateDepGraph { path: &path_buf, err }) + sess.dcx().emit_fatal(diagnostics::CreateDepGraph { path: &path_buf, err }) }); file_format::write_file_header(&mut encoder, sess); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 8360b352a2643..544ab66766f39 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -13,7 +13,7 @@ use super::data::*; use super::fs::*; use super::{clean, file_format, work_product}; use crate::assert_dep_graph::assert_dep_graph; -use crate::errors; +use crate::diagnostics; /// Saves and writes the [`DepGraph`] to the file system. /// @@ -45,7 +45,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { move || { sess.time("incr_comp_persist_dep_graph", || { if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { - sess.dcx().emit_err(errors::MoveDepGraph { + sess.dcx().emit_err(diagnostics::MoveDepGraph { from: &staging_dep_graph_path, to: &dep_graph_path, err, diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 64dae5c0869b9..910860bfafd6e 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -11,7 +11,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; use tracing::debug; -use crate::errors; +use crate::diagnostics; use crate::persist::fs::*; /// Copies a CGU work product to the incremental compilation directory, so next compilation can @@ -40,7 +40,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( let _ = saved_files.insert(ext.to_string(), file_name); } Err(err) => { - sess.dcx().emit_warn(errors::CopyWorkProductToCache { + sess.dcx().emit_warn(diagnostics::CopyWorkProductToCache { from: path, to: &path_in_incr_dir, err, @@ -60,7 +60,7 @@ pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduc for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { - sess.dcx().emit_warn(errors::DeleteWorkProduct { path: &path, err }); + sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); } } } From 6e1de0759fc74a1c164b3f1bc1f09a90dcd211b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 2 Jun 2026 16:36:10 +0200 Subject: [PATCH 70/71] add pre-stabilization testing CI job - Moves the existing new solver stdlib testing to that job. Bootstrapping will also be need to be tested in the near future. - Also tests the polonius alpha, by running the UI tests under the polonius compare-mode --- .../x86_64-gnu-pre-stabilization/Dockerfile | 29 +++++++++++++++++++ src/ci/docker/scripts/x86_64-gnu-llvm.sh | 5 ---- .../scripts/x86_64-gnu-pre-stabilization.sh | 23 +++++++++++++++ src/ci/github-actions/jobs.yml | 8 +++++ 4 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-pre-stabilization/Dockerfile create mode 100755 src/ci/docker/scripts/x86_64-gnu-pre-stabilization.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-pre-stabilization/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-pre-stabilization/Dockerfile new file mode 100644 index 0000000000000..8b61470aeb5f1 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-pre-stabilization/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + mingw-w64 \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu" + +COPY scripts/x86_64-gnu-pre-stabilization.sh /scripts/ +ENV SCRIPT="/scripts/x86_64-gnu-pre-stabilization.sh" diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh index 79177b96a4afc..21e2146609213 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm.sh @@ -15,8 +15,3 @@ set -ex # Run the UI test suite in `--pass=check` mode, to ensure it continues to work. ../x.ps1 --stage 2 test tests/ui --pass=check --host='' --target=i686-unknown-linux-gnu - -# Rebuild the stdlib using the new trait solver, to ensure it doesn't regress -# until stabilization. -RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x --stage 1 build library \ - --host='' --target=i686-unknown-linux-gnu diff --git a/src/ci/docker/scripts/x86_64-gnu-pre-stabilization.sh b/src/ci/docker/scripts/x86_64-gnu-pre-stabilization.sh new file mode 100755 index 0000000000000..81c1c4356c342 --- /dev/null +++ b/src/ci/docker/scripts/x86_64-gnu-pre-stabilization.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -ex + +# This script tests features intended to be stabilized in 2026. We want to +# ensure they don't regress until then. + +# 1. For the new trait solver, we want to: +# - ensure it can build the standard library +# +# FIXME: we also need to ensure it actually bootstraps. + +RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x build library --stage 1 + +# 2. For the polonius alpha, we run the UI tests under the polonius +# compare-mode. +# +# Note that we keep the same rustflags to avoid needing to rebuild any stage 1 +# artifacts from the previous command. It also tests both features at the same +# time. + +RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x test tests/ui \ + --compare-mode polonius --stage 1 diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 2bdf83a9c006b..31e923f639f8a 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -152,6 +152,14 @@ pr: env: CODEGEN_BACKENDS: gcc <<: *job-linux-4c + + # This job tests features we want to stabilize soon, to ensure they don't + # regress. + - name: x86_64-gnu-pre-stabilization + doc_url: https://rustc-dev-guide.rust-lang.org/tests/pre-stabilization-ci-job.html + env: + CODEGEN_BACKENDS: llvm + <<: *job-linux-4c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating From 5c35f312978040d33feb6babc64381b4920da022 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Fri, 12 Jun 2026 15:05:43 +0200 Subject: [PATCH 71/71] Fuse the two loops and do all region initialisation in one shot --- .../rustc_borrowck/src/region_infer/mod.rs | 27 ++++++++++--------- .../rustc_borrowck/src/region_infer/values.rs | 14 +++++----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c0b25b5b59f4b..5e56ae80ff5da 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -364,17 +364,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut scc_values = RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - // Initializes the region variables for each universally - // quantified region (lifetime parameter). The first N variables - // always correspond to the regions appearing in the function - // signature (both named and anonymous) and in where-clauses. + // Initializes the region variables with their initial live points. for (region, definition) in definitions.iter_enumerated() { let scc = constraint_sccs.scc(region); + // For each universally quantified region (lifetime parameter). The + // first N variables always correspond to the regions appearing in the + // function signature (both named and anonymous) and in where-clauses. match definition.origin { + // For each free, universally quantified region X: NllRegionVariableOrigin::FreeRegion => { - // For each free, universally quantified region X: - // Add all nodes in the CFG to liveness constraints liveness_constraints.add_all_points(region); @@ -383,22 +382,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { } NllRegionVariableOrigin::Placeholder(placeholder) => { - scc_values.add_placeholder(scc, placeholder) + scc_values.add_placeholder(scc, placeholder); } NllRegionVariableOrigin::Existential { .. } => { // For existential, regions, nothing to do. } } - } - for (region, liveness) in liveness_constraints.regions_and_liveness() { // Initially copy the liveness constraints of any region that // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. // - // These values will later be propagated during [`Self::propagate_constraints()`] - scc_values.merge_liveness(constraint_sccs.scc(region), liveness); - // Note: this includes the liveness values we just initialised above! + // These values will later be propagated during [`Self::propagate_constraints()`]. + // The values include any live-at-all-points constraints added above + // for free regions. + if let Some(liveness) = liveness_constraints.point_liveness(region) { + scc_values.merge_liveness(scc, liveness) + } } Self { @@ -556,7 +556,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // To propagate constraints, we walk the DAG induced by the // SCC. For each SCC `A`, we visit its successors and compute // their values, then we union all those values to get our - // own. + // own. This one-shot approach works because iteration is in + // dependency order. I.e. a chain A: B: C will visit C, B, A. for scc_a in self.constraint_sccs.all_sccs() { // Walk each SCC `B` such that `A: B`... for &scc_b in self.constraint_sccs.successors(scc_a) { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 5370d1c7537b3..27ee3f6176ee0 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -95,11 +95,10 @@ impl LivenessValues { } } - /// Iterate through each region that has a value in this set. - pub(crate) fn regions_and_liveness( - &self, - ) -> impl Iterator)> { - self.points().iter_enumerated() + /// Get the liveness status of a region `r`, if any. + /// Panics if liveness data is not tracked for any region. + pub(crate) fn point_liveness(&self, region: RegionVid) -> Option<&IntervalSet> { + self.points().row(region) } /// Iterate through each region that has a value in this set. @@ -168,13 +167,12 @@ impl LivenessValues { /// [`point`][rustc_mir_dataflow::points::PointIndex]. #[inline] pub(crate) fn is_live_at_point(&self, region: RegionVid, point: PointIndex) -> bool { - self.points().row(region).is_some_and(|r| r.contains(point)) + self.point_liveness(region).is_some_and(|r| r.contains(point)) } /// Returns an iterator of all the points where `region` is live. fn live_points(&self, region: RegionVid) -> impl Iterator { - self.points() - .row(region) + self.point_liveness(region) .into_iter() .flat_map(|set| set.iter()) .take_while(|&p| self.location_map.point_in_range(p))