From a22eaef5e5772feb3115a2425ff7fd8c5d2f6a74 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 30 Apr 2026 19:36:48 -0400 Subject: [PATCH 1/8] Add `-Zcodegen-emit-retag` with nop retags. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 73 +++++++++++++++---- compiler/rustc_codegen_ssa/src/mir/mod.rs | 65 ++++++++++++++++- compiler/rustc_codegen_ssa/src/mir/retag.rs | 39 ++++++++++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 7 +- .../rustc_codegen_ssa/src/mir/statement.rs | 26 ++++++- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_session/src/config.rs | 23 ++++-- compiler/rustc_session/src/options.rs | 27 +++++++ compiler/rustc_session/src/session.rs | 2 + 9 files changed, 237 insertions(+), 26 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/mir/retag.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f4e08e08ef8db..049a17f9f508c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -23,6 +23,7 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; +use crate::mir::retag; use crate::traits::*; use crate::{MemFlags, meth}; @@ -263,6 +264,12 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx.lifetime_end(tmp, size); } fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret); + + // If the return value has variants that needed to be retagged, + // then we might be in a different basic block now. + // Update the cached block for `target` to point to this new + // block, where codegen will continue. + fx.cached_llbbs[target] = CachedLlbb::Some(bx.llbb()); } MergingSucc::False } else { @@ -1054,21 +1061,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); + let needs_retag = retag::place_needs_retag(&destination); + let return_dest = if result_layout.is_zst() { ReturnDest::Nothing } else if let Some(index) = destination.as_local() { match self.locals[index] { - LocalRef::Place(dest) => ReturnDest::Store(dest), + LocalRef::Place(dest) => ReturnDest::Store { dest, needs_retag }, LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), LocalRef::PendingOperand => { // Handle temporary places, specifically `Operand` ones, as // they don't have `alloca`s. - ReturnDest::DirectOperand(index) + ReturnDest::DirectOperand { index, needs_retag } } LocalRef::Operand(_) => bug!("place local already assigned to"), } } else { - ReturnDest::Store(self.codegen_place(bx, destination.as_ref())) + ReturnDest::Store { + dest: self.codegen_place(bx, destination.as_ref()), + needs_retag, + } }; let args = @@ -2097,6 +2109,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if fn_ret.is_ignore() { return ReturnDest::Nothing; } + + let needs_retag = retag::place_needs_retag(&dest); + let dest = if let Some(index) = dest.as_local() { match self.locals[index] { LocalRef::Place(dest) => dest, @@ -2110,9 +2125,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let tmp = PlaceRef::alloca(bx, fn_ret.layout); tmp.storage_live(bx); llargs.push(tmp.val.llval); - ReturnDest::IndirectOperand(tmp, index) + ReturnDest::IndirectOperand { tmp, index, needs_retag } } else { - ReturnDest::DirectOperand(index) + ReturnDest::DirectOperand { index, needs_retag } }; } LocalRef::Operand(_) => { @@ -2135,7 +2150,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { llargs.push(dest.val.llval); ReturnDest::Nothing } else { - ReturnDest::Store(dest) + ReturnDest::Store { dest, needs_retag } } } @@ -2148,19 +2163,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { llval: Bx::Value, ) { use self::ReturnDest::*; - + let retags_enabled = bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some(); match dest { Nothing => (), - Store(dst) => bx.store_arg(ret_abi, llval, dst), - IndirectOperand(tmp, index) => { - let op = bx.load_operand(tmp); + Store { dest, needs_retag } => { + bx.store_arg(ret_abi, llval, dest); + if retags_enabled && needs_retag { + self.codegen_retag_place(bx, dest, false); + } + } + IndirectOperand { tmp, index, needs_retag } => { + let mut op = bx.load_operand(tmp); + if retags_enabled && needs_retag { + op = self.codegen_retag_operand(bx, op, false); + } tmp.storage_dead(bx); self.overwrite_local(index, LocalRef::Operand(op)); self.debug_introduce_local(bx, index); } - DirectOperand(index) => { + DirectOperand { index, needs_retag } => { // If there is a cast, we have to store and reload. - let op = if let PassMode::Cast { .. } = ret_abi.mode { + let mut op = if let PassMode::Cast { .. } = ret_abi.mode { let tmp = PlaceRef::alloca(bx, ret_abi.layout); tmp.storage_live(bx); bx.store_arg(ret_abi, llval, tmp); @@ -2170,6 +2193,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout) }; + if retags_enabled && needs_retag { + op = self.codegen_retag_operand(bx, op, false); + } self.overwrite_local(index, LocalRef::Operand(op)); self.debug_introduce_local(bx, index); } @@ -2181,11 +2207,28 @@ enum ReturnDest<'tcx, V> { /// Do nothing; the return value is indirect or ignored. Nothing, /// Store the return value to the pointer. - Store(PlaceRef<'tcx, V>), + Store { + /// The destination place + dest: PlaceRef<'tcx, V>, + /// If this place needs to be retagged + needs_retag: bool, + }, /// Store an indirect return value to an operand local place. - IndirectOperand(PlaceRef<'tcx, V>, mir::Local), + IndirectOperand { + /// The temp place where the value is loaded from + tmp: PlaceRef<'tcx, V>, + /// The local that it is assigned to + index: mir::Local, + /// If this local needs to be retagged after the assignment + needs_retag: bool, + }, /// Store a direct return value to an operand local place. - DirectOperand(mir::Local), + DirectOperand { + /// The destination local + index: mir::Local, + /// If this local needs to be retagged after the assignment. + needs_retag: bool, + }, } fn load_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 84013a00d79df..119e3102cb009 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -23,6 +23,7 @@ mod locals; pub mod naked_asm; pub mod operand; pub mod place; +mod retag; mod rvalue; mod statement; @@ -401,7 +402,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return vec![]; } - let args = mir + let mut args = mir .args_iter() .enumerate() .map(|(arg_index, local)| { @@ -535,6 +536,40 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } }) .collect::>(); + if bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some() { + if let ty::InstanceKind::DropGlue(_, _) | ty::InstanceKind::AsyncDropGlue(_, _) = + fx.instance.def + { + // We need to special-case drop glue for now. The first argument is + // a raw pointer, but it needs to be treated as if it were `&mut _`. + args[0] = dropee_emit_retag(bx, fx, &args[0]); + } else { + args = args + .iter() + .map(|arg| match arg { + &LocalRef::Place(place_ref) => { + fx.codegen_retag_place(bx, place_ref, true); + LocalRef::Place(place_ref) + } + &LocalRef::UnsizedPlace(place_ref) => { + let operand = bx.load_operand(place_ref); + let retagged = fx.codegen_retag_operand(bx, operand, true); + assert!(matches!(retagged.val, OperandValue::Pair(_, _))); + retagged.val.store(bx, place_ref); + LocalRef::UnsizedPlace(place_ref) + } + &LocalRef::Operand(operand_ref) => { + let retagged = fx.codegen_retag_operand(bx, operand_ref, true); + LocalRef::Operand(retagged) + } + LocalRef::PendingOperand => LocalRef::PendingOperand, + }) + .collect::>(); + } + // If we branched during retagging, then we need to update the + // start block to the new location. + fx.cached_llbbs[mir::START_BLOCK] = CachedLlbb::Some(bx.llbb()); + } if fx.instance.def.requires_caller_location(bx.tcx()) { let mir_args = if let Some(num_untupled) = num_untupled { @@ -612,3 +647,31 @@ fn find_cold_blocks<'tcx>( cold_blocks } + +fn dropee_emit_retag<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + local: &LocalRef<'tcx, Bx::Value>, +) -> LocalRef<'tcx, Bx::Value> { + // We have `*mut _` as our first argument + if let &LocalRef::Operand(OperandRef { val, layout, move_annotation }) = local { + if layout.ty.is_raw_ptr() + && let Some(deref_ty) = layout.ty.builtin_deref(true) + { + // Create `&mut _` + let lifetime = bx.tcx().lifetimes.re_erased; + let subst_ty_kind = ty::Ref(lifetime, deref_ty, ty::Mutability::Mut); + let subst_ty = bx.tcx().mk_ty_from_kind(subst_ty_kind); + let subst_layout = bx.layout_of(subst_ty); + + // We want the same operand value, but use the reference type for it. + let operand_ref = OperandRef { val, layout: subst_layout, move_annotation }; + + let retagged = fx.codegen_retag_operand(bx, operand_ref, true); + + // Return the retagged parameter, but use the original layout now. + return LocalRef::Operand(OperandRef { val: retagged.val, layout, move_annotation }); + } + } + bug!("dropee isn't a raw pointer") +} diff --git a/compiler/rustc_codegen_ssa/src/mir/retag.rs b/compiler/rustc_codegen_ssa/src/mir/retag.rs new file mode 100644 index 0000000000000..ab47ad611eac6 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -0,0 +1,39 @@ +//! Experimental support for emitting retags as function calls in generated code. + +use rustc_middle::mir::{Place, Rvalue, WithRetag}; + +use crate::mir::FunctionCx; +use crate::mir::operand::OperandRef; +use crate::mir::place::PlaceRef; +use crate::traits::BuilderMethods; + +pub(crate) fn place_needs_retag(place: &Place<'_>) -> bool { + // We're not interested in tracking stores to "outside" locations + !place.is_indirect_first_projection() +} + +pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool { + // `Ref` has its own internal retagging + !matches!(rvalue, Rvalue::Ref(..)) && !matches!(rvalue, Rvalue::Use(.., WithRetag::No)) +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + /// Retags the pointers within an [`OperandRef`]. + pub(crate) fn codegen_retag_operand( + &mut self, + _bx: &mut Bx, + operand: OperandRef<'tcx, Bx::Value>, + _is_fn_entry: bool, + ) -> OperandRef<'tcx, Bx::Value> { + operand + } + + /// Retags the pointers within a [`PlaceRef`]. + pub(crate) fn codegen_retag_place( + &mut self, + _bx: &mut Bx, + _place_ref: PlaceRef<'tcx, Bx::Value>, + _is_fn_entry: bool, + ) { + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 90ac8c89ba9ad..469d6af12923e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -522,7 +522,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, bk.to_mutbl_lossy()) }; - self.codegen_place_to_pointer(bx, place, mk_ref) + let op = self.codegen_place_to_pointer(bx, place, mk_ref); + if self.cx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some() { + self.codegen_retag_operand(bx, op, false) + } else { + op + } } // Note: Exclusive reborrowing is always equal to a memcpy, as the types do not change. diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index afbef86bd089e..2d98108497aea 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -3,6 +3,7 @@ use rustc_middle::{bug, span_bug, ty}; use tracing::instrument; use super::{FunctionCx, LocalRef}; +use crate::mir::retag; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -12,9 +13,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, statement.source_info); match statement.kind { mir::StatementKind::Assign((ref place, ref rvalue)) => { + let needs_retag = bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some() + && retag::rvalue_needs_retag(rvalue) + && retag::place_needs_retag(place); + if let Some(index) = place.as_local() { match self.locals[index] { - LocalRef::Place(cg_dest) => self.codegen_rvalue(bx, cg_dest, rvalue), + LocalRef::Place(cg_dest) => { + self.codegen_rvalue(bx, cg_dest, rvalue); + if needs_retag { + self.codegen_retag_place(bx, cg_dest, false); + } + } LocalRef::UnsizedPlace(cg_indirect_dest) => { let ty = cg_indirect_dest.layout.ty; span_bug!( @@ -24,7 +34,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); } LocalRef::PendingOperand => { - let operand = self.codegen_rvalue_operand(bx, rvalue); + let mut operand = self.codegen_rvalue_operand(bx, rvalue); + if needs_retag { + operand = self.codegen_retag_operand(bx, operand, false); + } self.overwrite_local(index, LocalRef::Operand(operand)); self.debug_introduce_local(bx, index); } @@ -39,12 +52,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // If the type is zero-sized, it's already been set here, // but we still need to make sure we codegen the operand - self.codegen_rvalue_operand(bx, rvalue); + // and emit a retag. + let operand = self.codegen_rvalue_operand(bx, rvalue); + if needs_retag { + self.codegen_retag_operand(bx, operand, false); + } } } } else { let cg_dest = self.codegen_place(bx, place.as_ref()); self.codegen_rvalue(bx, cg_dest, rvalue); + if needs_retag { + self.codegen_retag_place(bx, cg_dest, false); + } } } mir::StatementKind::SetDiscriminant { ref place, variant_index } => { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0837e7767605c..17175bba3eb4d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -772,6 +772,7 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); + tracked!(codegen_emit_retag, Some(CodegenRetagOptions::default())); tracked!( coverage_options, CoverageOptions { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2040cd48ec44f..83f70ae7032d1 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -200,6 +200,15 @@ pub enum Offload { Test, } +/// The different settings that the `-Z codegen-emit-retag` flag can have. +#[derive(Copy, Clone, Debug, Default, PartialEq, Hash, Encodable, Decodable)] +pub struct CodegenRetagOptions { + /// Track interior mutable data on the level of references, instead of on the byte level. + pub no_precise_im: bool, + /// Track `UnsafePinned` data on the level of references, instead of on the byte level. + pub no_precise_pin: bool, +} + /// The different settings that the `-Z autodiff` flag can have. #[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] pub enum AutoDiff { @@ -3048,12 +3057,13 @@ pub(crate) mod dep_tracking { }; use super::{ - AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CoverageOptions, - CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, - InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, - LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, - OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, - SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions, + CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, + FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, + LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, + OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3157,6 +3167,7 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, Align, + CodegenRetagOptions ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aa9331ee8f659..323fd1b35700b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -783,6 +783,8 @@ mod desc { pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub(crate) const parse_instrument_coverage: &str = parse_bool; pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition`"; + pub(crate) const parse_codegen_retag_options: &str = + "either no value or a comma-separated list of settings: `no-precise-im`, `no-precise-pin`"; pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -1523,6 +1525,29 @@ pub mod parse { true } + pub(crate) fn parse_codegen_retag_options( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + let Some(v) = v else { return true }; + let mut no_precise_im = false; + let mut no_precise_pin = false; + + for option in v.split(',') { + match option { + "no-precise-im" => { + no_precise_im = true; + } + "no-precise-pin" => { + no_precise_pin = true; + } + _ => return false, + } + } + *slot = Some(CodegenRetagOptions { no_precise_im, no_precise_pin }); + true + } + pub(crate) fn parse_coverage_options(slot: &mut CoverageOptions, v: Option<&str>) -> bool { let Some(v) = v else { return true }; @@ -2242,6 +2267,8 @@ options! { "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), + codegen_emit_retag: Option = (None, parse_codegen_retag_options, [TRACKED], + "emit retag function calls in generated code"), codegen_source_order: bool = (false, parse_bool, [UNTRACKED], "emit mono items in the order of spans in source files (default: no)"), contract_checks: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 003164e8f9054..1db0a259e7155 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -550,6 +550,8 @@ impl Session { // HWAddressSanitizer and KernelHWAddressSanitizer will use lifetimes to detect use after // scope bugs in the future. || self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS) + // Lifetimes are necessary for retagging semantics. + || self.opts.unstable_opts.codegen_emit_retag.is_some() } pub fn diagnostic_width(&self) -> usize { From c8d945e34610bd017a2d1c6230b3271a0965f1e8 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Tue, 5 May 2026 18:49:01 -0400 Subject: [PATCH 2/8] Add missing import. --- compiler/rustc_interface/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 17175bba3eb4d..906b444cce605 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -10,9 +10,9 @@ use rustc_errors::ColorConfig; use rustc_errors::emitter::HumanReadableErrorType; use rustc_hir::attrs::{CollapseMacroDebuginfo, NativeLibKind}; use rustc_session::config::{ - AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CoverageLevel, CoverageOptions, - DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, - FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, + AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CodegenRetagOptions, CoverageLevel, + CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, + Externs, FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, From ae7078aa157ac33dca3b0a1d17de75b6aed6852b Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 7 May 2026 13:02:23 -0400 Subject: [PATCH 3/8] Remove special handling for drop glue. --- compiler/rustc_codegen_ssa/src/mir/mod.rs | 78 ++++++----------------- 1 file changed, 21 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 119e3102cb009..f8c651c38a617 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -537,35 +537,27 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }) .collect::>(); if bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some() { - if let ty::InstanceKind::DropGlue(_, _) | ty::InstanceKind::AsyncDropGlue(_, _) = - fx.instance.def - { - // We need to special-case drop glue for now. The first argument is - // a raw pointer, but it needs to be treated as if it were `&mut _`. - args[0] = dropee_emit_retag(bx, fx, &args[0]); - } else { - args = args - .iter() - .map(|arg| match arg { - &LocalRef::Place(place_ref) => { - fx.codegen_retag_place(bx, place_ref, true); - LocalRef::Place(place_ref) - } - &LocalRef::UnsizedPlace(place_ref) => { - let operand = bx.load_operand(place_ref); - let retagged = fx.codegen_retag_operand(bx, operand, true); - assert!(matches!(retagged.val, OperandValue::Pair(_, _))); - retagged.val.store(bx, place_ref); - LocalRef::UnsizedPlace(place_ref) - } - &LocalRef::Operand(operand_ref) => { - let retagged = fx.codegen_retag_operand(bx, operand_ref, true); - LocalRef::Operand(retagged) - } - LocalRef::PendingOperand => LocalRef::PendingOperand, - }) - .collect::>(); - } + args = args + .iter() + .map(|arg| match arg { + &LocalRef::Place(place_ref) => { + fx.codegen_retag_place(bx, place_ref, true); + LocalRef::Place(place_ref) + } + &LocalRef::UnsizedPlace(place_ref) => { + let operand = bx.load_operand(place_ref); + let retagged = fx.codegen_retag_operand(bx, operand, true); + assert!(matches!(retagged.val, OperandValue::Pair(_, _))); + retagged.val.store(bx, place_ref); + LocalRef::UnsizedPlace(place_ref) + } + &LocalRef::Operand(operand_ref) => { + let retagged = fx.codegen_retag_operand(bx, operand_ref, true); + LocalRef::Operand(retagged) + } + LocalRef::PendingOperand => LocalRef::PendingOperand, + }) + .collect::>(); // If we branched during retagging, then we need to update the // start block to the new location. fx.cached_llbbs[mir::START_BLOCK] = CachedLlbb::Some(bx.llbb()); @@ -647,31 +639,3 @@ fn find_cold_blocks<'tcx>( cold_blocks } - -fn dropee_emit_retag<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - fx: &mut FunctionCx<'a, 'tcx, Bx>, - local: &LocalRef<'tcx, Bx::Value>, -) -> LocalRef<'tcx, Bx::Value> { - // We have `*mut _` as our first argument - if let &LocalRef::Operand(OperandRef { val, layout, move_annotation }) = local { - if layout.ty.is_raw_ptr() - && let Some(deref_ty) = layout.ty.builtin_deref(true) - { - // Create `&mut _` - let lifetime = bx.tcx().lifetimes.re_erased; - let subst_ty_kind = ty::Ref(lifetime, deref_ty, ty::Mutability::Mut); - let subst_ty = bx.tcx().mk_ty_from_kind(subst_ty_kind); - let subst_layout = bx.layout_of(subst_ty); - - // We want the same operand value, but use the reference type for it. - let operand_ref = OperandRef { val, layout: subst_layout, move_annotation }; - - let retagged = fx.codegen_retag_operand(bx, operand_ref, true); - - // Return the retagged parameter, but use the original layout now. - return LocalRef::Operand(OperandRef { val: retagged.val, layout, move_annotation }); - } - } - bug!("dropee isn't a raw pointer") -} From 0a470b6bc9dd503a8d7a00bcb55149d91b4ee3af Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Tue, 19 May 2026 12:16:56 -0400 Subject: [PATCH 4/8] Do not skip retagging indirect places. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 64 ++++++------------- compiler/rustc_codegen_ssa/src/mir/retag.rs | 7 +- .../rustc_codegen_ssa/src/mir/statement.rs | 3 +- 3 files changed, 21 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 049a17f9f508c..89b58b32a2527 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -23,7 +23,6 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; -use crate::mir::retag; use crate::traits::*; use crate::{MemFlags, meth}; @@ -265,7 +264,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret); - // If the return value has variants that needed to be retagged, + // If the return value was retagged as it was stored, // then we might be in a different basic block now. // Update the cached block for `target` to point to this new // block, where codegen will continue. @@ -1061,26 +1060,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); - let needs_retag = retag::place_needs_retag(&destination); - let return_dest = if result_layout.is_zst() { ReturnDest::Nothing } else if let Some(index) = destination.as_local() { match self.locals[index] { - LocalRef::Place(dest) => ReturnDest::Store { dest, needs_retag }, + LocalRef::Place(dest) => ReturnDest::Store(dest), LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), LocalRef::PendingOperand => { // Handle temporary places, specifically `Operand` ones, as // they don't have `alloca`s. - ReturnDest::DirectOperand { index, needs_retag } + ReturnDest::DirectOperand(index) } LocalRef::Operand(_) => bug!("place local already assigned to"), } } else { - ReturnDest::Store { - dest: self.codegen_place(bx, destination.as_ref()), - needs_retag, - } + ReturnDest::Store(self.codegen_place(bx, destination.as_ref())) }; let args = @@ -2109,9 +2103,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if fn_ret.is_ignore() { return ReturnDest::Nothing; } - - let needs_retag = retag::place_needs_retag(&dest); - let dest = if let Some(index) = dest.as_local() { match self.locals[index] { LocalRef::Place(dest) => dest, @@ -2125,9 +2116,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let tmp = PlaceRef::alloca(bx, fn_ret.layout); tmp.storage_live(bx); llargs.push(tmp.val.llval); - ReturnDest::IndirectOperand { tmp, index, needs_retag } + ReturnDest::IndirectOperand(tmp, index) } else { - ReturnDest::DirectOperand { index, needs_retag } + ReturnDest::DirectOperand(index) }; } LocalRef::Operand(_) => { @@ -2150,7 +2141,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { llargs.push(dest.val.llval); ReturnDest::Nothing } else { - ReturnDest::Store { dest, needs_retag } + ReturnDest::Store(dest) } } @@ -2166,22 +2157,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let retags_enabled = bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some(); match dest { Nothing => (), - Store { dest, needs_retag } => { - bx.store_arg(ret_abi, llval, dest); - if retags_enabled && needs_retag { - self.codegen_retag_place(bx, dest, false); + Store(dst) => { + bx.store_arg(ret_abi, llval, dst); + if retags_enabled { + self.codegen_retag_place(bx, dst, false); } } - IndirectOperand { tmp, index, needs_retag } => { + IndirectOperand(tmp, index) => { let mut op = bx.load_operand(tmp); - if retags_enabled && needs_retag { + tmp.storage_dead(bx); + if retags_enabled { op = self.codegen_retag_operand(bx, op, false); } - tmp.storage_dead(bx); self.overwrite_local(index, LocalRef::Operand(op)); self.debug_introduce_local(bx, index); } - DirectOperand { index, needs_retag } => { + DirectOperand(index) => { // If there is a cast, we have to store and reload. let mut op = if let PassMode::Cast { .. } = ret_abi.mode { let tmp = PlaceRef::alloca(bx, ret_abi.layout); @@ -2193,7 +2184,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout) }; - if retags_enabled && needs_retag { + if retags_enabled { op = self.codegen_retag_operand(bx, op, false); } self.overwrite_local(index, LocalRef::Operand(op)); @@ -2207,28 +2198,11 @@ enum ReturnDest<'tcx, V> { /// Do nothing; the return value is indirect or ignored. Nothing, /// Store the return value to the pointer. - Store { - /// The destination place - dest: PlaceRef<'tcx, V>, - /// If this place needs to be retagged - needs_retag: bool, - }, + Store(PlaceRef<'tcx, V>), /// Store an indirect return value to an operand local place. - IndirectOperand { - /// The temp place where the value is loaded from - tmp: PlaceRef<'tcx, V>, - /// The local that it is assigned to - index: mir::Local, - /// If this local needs to be retagged after the assignment - needs_retag: bool, - }, + IndirectOperand(PlaceRef<'tcx, V>, mir::Local), /// Store a direct return value to an operand local place. - DirectOperand { - /// The destination local - index: mir::Local, - /// If this local needs to be retagged after the assignment. - needs_retag: bool, - }, + DirectOperand(mir::Local), } fn load_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/retag.rs b/compiler/rustc_codegen_ssa/src/mir/retag.rs index ab47ad611eac6..a08a434326bb5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/retag.rs +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -1,17 +1,12 @@ //! Experimental support for emitting retags as function calls in generated code. -use rustc_middle::mir::{Place, Rvalue, WithRetag}; +use rustc_middle::mir::{Rvalue, WithRetag}; use crate::mir::FunctionCx; use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::traits::BuilderMethods; -pub(crate) fn place_needs_retag(place: &Place<'_>) -> bool { - // We're not interested in tracking stores to "outside" locations - !place.is_indirect_first_projection() -} - pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool { // `Ref` has its own internal retagging !matches!(rvalue, Rvalue::Ref(..)) && !matches!(rvalue, Rvalue::Use(.., WithRetag::No)) diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 2d98108497aea..433964218e43b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -14,8 +14,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match statement.kind { mir::StatementKind::Assign((ref place, ref rvalue)) => { let needs_retag = bx.tcx().sess.opts.unstable_opts.codegen_emit_retag.is_some() - && retag::rvalue_needs_retag(rvalue) - && retag::place_needs_retag(place); + && retag::rvalue_needs_retag(rvalue); if let Some(index) = place.as_local() { match self.locals[index] { From 23602aabbb4f23e5a25801ae015af94367029050 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Wed, 20 May 2026 12:43:13 -0400 Subject: [PATCH 5/8] Ensure that providing no option still enables the flag. --- compiler/rustc_session/src/options.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 323fd1b35700b..d9b8deec6bc49 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1529,19 +1529,19 @@ pub mod parse { slot: &mut Option, v: Option<&str>, ) -> bool { - let Some(v) = v else { return true }; let mut no_precise_im = false; let mut no_precise_pin = false; - - for option in v.split(',') { - match option { - "no-precise-im" => { - no_precise_im = true; - } - "no-precise-pin" => { - no_precise_pin = true; + if let Some(opt_list) = v.map(|s| s.split(',')) { + for opt in opt_list { + match opt { + "no-precise-im" => { + no_precise_im = true; + } + "no-precise-pin" => { + no_precise_pin = true; + } + _ => return false, } - _ => return false, } } *slot = Some(CodegenRetagOptions { no_precise_im, no_precise_pin }); From 8a29c826fe000dc47f5b09adb13f027e813a4252 Mon Sep 17 00:00:00 2001 From: zedddie Date: Wed, 3 Jun 2026 02:31:08 +0200 Subject: [PATCH 6/8] move batch --- .../assoc-type-unsatisfied-bound.rs} | 0 .../assoc-type-unsatisfied-bound.stderr} | 0 tests/ui/{issues/issue-18845.rs => box/box-deref-match-arm.rs} | 0 .../auxiliary/inline-fn-with-trait-method-as-value.rs} | 0 .../inline-fn-with-trait-method-as-value.rs} | 0 .../{issues/issue-18919.rs => dst/dyn-fn-type-in-generic-enum.rs} | 0 .../issue-18919.stderr => dst/dyn-fn-type-in-generic-enum.stderr} | 0 .../dyn-incompatible-supertrait.rs} | 0 .../dyn-incompatible-supertrait.stderr} | 0 .../auxiliary/duplicate-rlib-crate-name-precedence-1.rs} | 0 .../auxiliary/duplicate-rlib-crate-name-precedence-2.rs} | 0 .../duplicate-rlib-crate-name-precedence.rs} | 0 .../issue-18859.rs => macros/module-path-in-nested-modules.rs} | 0 .../object/trait-object-with-send-supertrait.rs} | 0 .../auxiliary/cross-crate-generic-fn-in-closure.rs} | 0 .../cross-crate-generic-fn-in-closure.rs} | 0 .../impl-method-where-clause-resolution.rs} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-18611.rs => associated-types/assoc-type-unsatisfied-bound.rs} (100%) rename tests/ui/{issues/issue-18611.stderr => associated-types/assoc-type-unsatisfied-bound.stderr} (100%) rename tests/ui/{issues/issue-18845.rs => box/box-deref-match-arm.rs} (100%) rename tests/ui/{issues/auxiliary/issue-18501.rs => cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs} (100%) rename tests/ui/{issues/issue-18501.rs => cross-crate/inline-fn-with-trait-method-as-value.rs} (100%) rename tests/ui/{issues/issue-18919.rs => dst/dyn-fn-type-in-generic-enum.rs} (100%) rename tests/ui/{issues/issue-18919.stderr => dst/dyn-fn-type-in-generic-enum.stderr} (100%) rename tests/ui/{issues/issue-18959.rs => dyn-compatibility/dyn-incompatible-supertrait.rs} (100%) rename tests/ui/{issues/issue-18959.stderr => dyn-compatibility/dyn-incompatible-supertrait.stderr} (100%) rename tests/ui/{issues/auxiliary/issue-18913-1.rs => linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs} (100%) rename tests/ui/{issues/auxiliary/issue-18913-2.rs => linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs} (100%) rename tests/ui/{issues/issue-18913.rs => linking/duplicate-rlib-crate-name-precedence.rs} (100%) rename tests/ui/{issues/issue-18859.rs => macros/module-path-in-nested-modules.rs} (100%) rename tests/ui/{issues/issue-18988.rs => traits/object/trait-object-with-send-supertrait.rs} (100%) rename tests/ui/{issues/auxiliary/issue-18711.rs => unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs} (100%) rename tests/ui/{issues/issue-18711.rs => unboxed-closures/cross-crate-generic-fn-in-closure.rs} (100%) rename tests/ui/{issues/issue-18906.rs => where-clauses/impl-method-where-clause-resolution.rs} (100%) diff --git a/tests/ui/issues/issue-18611.rs b/tests/ui/associated-types/assoc-type-unsatisfied-bound.rs similarity index 100% rename from tests/ui/issues/issue-18611.rs rename to tests/ui/associated-types/assoc-type-unsatisfied-bound.rs diff --git a/tests/ui/issues/issue-18611.stderr b/tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr similarity index 100% rename from tests/ui/issues/issue-18611.stderr rename to tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr diff --git a/tests/ui/issues/issue-18845.rs b/tests/ui/box/box-deref-match-arm.rs similarity index 100% rename from tests/ui/issues/issue-18845.rs rename to tests/ui/box/box-deref-match-arm.rs diff --git a/tests/ui/issues/auxiliary/issue-18501.rs b/tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-18501.rs rename to tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs diff --git a/tests/ui/issues/issue-18501.rs b/tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs similarity index 100% rename from tests/ui/issues/issue-18501.rs rename to tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs diff --git a/tests/ui/issues/issue-18919.rs b/tests/ui/dst/dyn-fn-type-in-generic-enum.rs similarity index 100% rename from tests/ui/issues/issue-18919.rs rename to tests/ui/dst/dyn-fn-type-in-generic-enum.rs diff --git a/tests/ui/issues/issue-18919.stderr b/tests/ui/dst/dyn-fn-type-in-generic-enum.stderr similarity index 100% rename from tests/ui/issues/issue-18919.stderr rename to tests/ui/dst/dyn-fn-type-in-generic-enum.stderr diff --git a/tests/ui/issues/issue-18959.rs b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs similarity index 100% rename from tests/ui/issues/issue-18959.rs rename to tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs diff --git a/tests/ui/issues/issue-18959.stderr b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr similarity index 100% rename from tests/ui/issues/issue-18959.stderr rename to tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr diff --git a/tests/ui/issues/auxiliary/issue-18913-1.rs b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-18913-1.rs rename to tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs diff --git a/tests/ui/issues/auxiliary/issue-18913-2.rs b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-18913-2.rs rename to tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs diff --git a/tests/ui/issues/issue-18913.rs b/tests/ui/linking/duplicate-rlib-crate-name-precedence.rs similarity index 100% rename from tests/ui/issues/issue-18913.rs rename to tests/ui/linking/duplicate-rlib-crate-name-precedence.rs diff --git a/tests/ui/issues/issue-18859.rs b/tests/ui/macros/module-path-in-nested-modules.rs similarity index 100% rename from tests/ui/issues/issue-18859.rs rename to tests/ui/macros/module-path-in-nested-modules.rs diff --git a/tests/ui/issues/issue-18988.rs b/tests/ui/traits/object/trait-object-with-send-supertrait.rs similarity index 100% rename from tests/ui/issues/issue-18988.rs rename to tests/ui/traits/object/trait-object-with-send-supertrait.rs diff --git a/tests/ui/issues/auxiliary/issue-18711.rs b/tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-18711.rs rename to tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs diff --git a/tests/ui/issues/issue-18711.rs b/tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs similarity index 100% rename from tests/ui/issues/issue-18711.rs rename to tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs diff --git a/tests/ui/issues/issue-18906.rs b/tests/ui/where-clauses/impl-method-where-clause-resolution.rs similarity index 100% rename from tests/ui/issues/issue-18906.rs rename to tests/ui/where-clauses/impl-method-where-clause-resolution.rs From b972e77b789b4a825ca301d8ac5d8f1bca9e9149 Mon Sep 17 00:00:00 2001 From: zedddie Date: Wed, 3 Jun 2026 02:40:54 +0200 Subject: [PATCH 7/8] bless batch --- .../assoc-type-unsatisfied-bound.rs | 2 ++ .../assoc-type-unsatisfied-bound.stderr | 8 ++++---- tests/ui/box/box-deref-match-arm.rs | 11 +++++++---- .../inline-fn-with-trait-method-as-value.rs | 2 ++ .../inline-fn-with-trait-method-as-value.rs | 13 ++++++++----- tests/ui/dst/dyn-fn-type-in-generic-enum.rs | 2 ++ tests/ui/dst/dyn-fn-type-in-generic-enum.stderr | 6 +++--- .../dyn-incompatible-supertrait.rs | 2 ++ .../dyn-incompatible-supertrait.stderr | 12 ++++++------ .../duplicate-rlib-crate-name-precedence-1.rs | 2 ++ .../duplicate-rlib-crate-name-precedence-2.rs | 2 ++ .../linking/duplicate-rlib-crate-name-precedence.rs | 6 ++++-- tests/ui/macros/module-path-in-nested-modules.rs | 6 ++++-- .../object/trait-object-with-send-supertrait.rs | 2 ++ .../auxiliary/cross-crate-generic-fn-in-closure.rs | 2 ++ .../cross-crate-generic-fn-in-closure.rs | 11 ++++++----- .../impl-method-where-clause-resolution.rs | 2 ++ 17 files changed, 60 insertions(+), 31 deletions(-) diff --git a/tests/ui/associated-types/assoc-type-unsatisfied-bound.rs b/tests/ui/associated-types/assoc-type-unsatisfied-bound.rs index 57da57d83537c..76a93eed7b663 100644 --- a/tests/ui/associated-types/assoc-type-unsatisfied-bound.rs +++ b/tests/ui/associated-types/assoc-type-unsatisfied-bound.rs @@ -1,3 +1,5 @@ +//! Regression test for . + fn add_state(op: ::State) { //~^ ERROR `isize: HasState` is not satisfied //~| ERROR `isize: HasState` is not satisfied diff --git a/tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr b/tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr index 4fa699de63527..3e318fcac503a 100644 --- a/tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr +++ b/tests/ui/associated-types/assoc-type-unsatisfied-bound.stderr @@ -1,23 +1,23 @@ error[E0277]: the trait bound `isize: HasState` is not satisfied - --> $DIR/issue-18611.rs:1:18 + --> $DIR/assoc-type-unsatisfied-bound.rs:3:18 | LL | fn add_state(op: ::State) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasState` is not implemented for `isize` | help: this trait has no implementations, consider adding one - --> $DIR/issue-18611.rs:6:1 + --> $DIR/assoc-type-unsatisfied-bound.rs:8:1 | LL | trait HasState { | ^^^^^^^^^^^^^^ error[E0277]: the trait bound `isize: HasState` is not satisfied - --> $DIR/issue-18611.rs:1:18 + --> $DIR/assoc-type-unsatisfied-bound.rs:3:18 | LL | fn add_state(op: ::State) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasState` is not implemented for `isize` | help: this trait has no implementations, consider adding one - --> $DIR/issue-18611.rs:6:1 + --> $DIR/assoc-type-unsatisfied-bound.rs:8:1 | LL | trait HasState { | ^^^^^^^^^^^^^^ diff --git a/tests/ui/box/box-deref-match-arm.rs b/tests/ui/box/box-deref-match-arm.rs index c9dc175b10a8a..621a577afa580 100644 --- a/tests/ui/box/box-deref-match-arm.rs +++ b/tests/ui/box/box-deref-match-arm.rs @@ -1,8 +1,11 @@ +//! Regression test for . +//! +//! This used to generate invalid IR in that even if we took the +//! `false` branch we'd still try to free the Box from the other +//! arm. This was due to treating `*Box::new(9)` as an rvalue datum +//! instead of as a place. + //@ run-pass -// This used to generate invalid IR in that even if we took the -// `false` branch we'd still try to free the Box from the other -// arm. This was due to treating `*Box::new(9)` as an rvalue datum -// instead of as a place. fn test(foo: bool) -> u8 { match foo { diff --git a/tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs b/tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs index dd914b464fa58..dd90b4a082331 100644 --- a/tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs +++ b/tests/ui/cross-crate/auxiliary/inline-fn-with-trait-method-as-value.rs @@ -1,3 +1,5 @@ +//! Auxiliary crate for . + #![crate_type = "rlib"] struct Foo; diff --git a/tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs b/tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs index 54e53e434c465..7c68f29352493 100644 --- a/tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs +++ b/tests/ui/cross-crate/inline-fn-with-trait-method-as-value.rs @@ -1,11 +1,14 @@ +//! Regression test for . +//! +//! Test that we don't ICE when inlining a function from another +//! crate that uses a trait method as a value due to incorrectly +//! translating the def ID of the trait during AST decoding. + //@ run-pass -// Test that we don't ICE when inlining a function from another -// crate that uses a trait method as a value due to incorrectly -// translating the def ID of the trait during AST decoding. -//@ aux-build:issue-18501.rs +//@ aux-build:inline-fn-with-trait-method-as-value.rs -extern crate issue_18501 as issue; +extern crate inline_fn_with_trait_method_as_value as issue; fn main() { issue::pass_method(); diff --git a/tests/ui/dst/dyn-fn-type-in-generic-enum.rs b/tests/ui/dst/dyn-fn-type-in-generic-enum.rs index f06771e9ea59d..a4d07b01007dc 100644 --- a/tests/ui/dst/dyn-fn-type-in-generic-enum.rs +++ b/tests/ui/dst/dyn-fn-type-in-generic-enum.rs @@ -1,3 +1,5 @@ +//! Regression test for . + type FuncType<'f> = dyn Fn(&isize) -> isize + 'f; fn ho_func(f: Option) { diff --git a/tests/ui/dst/dyn-fn-type-in-generic-enum.stderr b/tests/ui/dst/dyn-fn-type-in-generic-enum.stderr index 714b6d7d86be3..dc672399e6800 100644 --- a/tests/ui/dst/dyn-fn-type-in-generic-enum.stderr +++ b/tests/ui/dst/dyn-fn-type-in-generic-enum.stderr @@ -1,17 +1,17 @@ error[E0277]: the size for values of type `dyn for<'a> Fn(&'a isize) -> isize` cannot be known at compilation time - --> $DIR/issue-18919.rs:3:15 + --> $DIR/dyn-fn-type-in-generic-enum.rs:5:15 | LL | fn ho_func(f: Option) { | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `dyn for<'a> Fn(&'a isize) -> isize` note: required by an implicit `Sized` bound in `Option` - --> $DIR/issue-18919.rs:7:13 + --> $DIR/dyn-fn-type-in-generic-enum.rs:9:13 | LL | enum Option { | ^ required by the implicit `Sized` requirement on this type parameter in `Option` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/issue-18919.rs:7:13 + --> $DIR/dyn-fn-type-in-generic-enum.rs:9:13 | LL | enum Option { | ^ this could be changed to `T: ?Sized`... diff --git a/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs index 415fe818f5308..90a30e7920544 100644 --- a/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs +++ b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.rs @@ -1,3 +1,5 @@ +//! Regression test for . + pub trait Foo { fn foo(&self, ext_thing: &T); } pub trait Bar: Foo { } impl Bar for T { } diff --git a/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr index df47d50a01979..34f1ed729c135 100644 --- a/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr +++ b/tests/ui/dyn-compatibility/dyn-incompatible-supertrait.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:11:12 + --> $DIR/dyn-incompatible-supertrait.rs:13:12 | LL | fn foo(b: &dyn Bar) { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-18959.rs:1:20 + --> $DIR/dyn-incompatible-supertrait.rs:3:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters @@ -15,14 +15,14 @@ LL | pub trait Bar: Foo { } = help: consider moving `foo` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:18:20 + --> $DIR/dyn-incompatible-supertrait.rs:20:20 | LL | let test: &dyn Bar = &mut thing; | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-18959.rs:1:20 + --> $DIR/dyn-incompatible-supertrait.rs:3:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters @@ -31,14 +31,14 @@ LL | pub trait Bar: Foo { } = help: consider moving `foo` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:20:9 + --> $DIR/dyn-incompatible-supertrait.rs:22:9 | LL | foo(test); | ^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-18959.rs:1:20 + --> $DIR/dyn-incompatible-supertrait.rs:3:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters diff --git a/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs index caa2c707b560d..7b2bfbba12e6e 100644 --- a/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs +++ b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-1.rs @@ -1,3 +1,5 @@ +//! Auxiliary crate for . + //@ no-prefer-dynamic #![crate_type = "rlib"] diff --git a/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs index 802f5ab3899bb..fc0d3d6ea4283 100644 --- a/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs +++ b/tests/ui/linking/auxiliary/duplicate-rlib-crate-name-precedence-2.rs @@ -1,3 +1,5 @@ +//! Auxiliary crate for . + //@ no-prefer-dynamic #![crate_type = "rlib"] diff --git a/tests/ui/linking/duplicate-rlib-crate-name-precedence.rs b/tests/ui/linking/duplicate-rlib-crate-name-precedence.rs index 7f9137d95c24f..5131df3903423 100644 --- a/tests/ui/linking/duplicate-rlib-crate-name-precedence.rs +++ b/tests/ui/linking/duplicate-rlib-crate-name-precedence.rs @@ -1,6 +1,8 @@ +//! Regression test for . + //@ run-pass -//@ aux-build:issue-18913-1.rs -//@ aux-build:issue-18913-2.rs +//@ aux-build:duplicate-rlib-crate-name-precedence-1.rs +//@ aux-build:duplicate-rlib-crate-name-precedence-2.rs extern crate foo; diff --git a/tests/ui/macros/module-path-in-nested-modules.rs b/tests/ui/macros/module-path-in-nested-modules.rs index 854b7ed62f05b..601599e92943e 100644 --- a/tests/ui/macros/module-path-in-nested-modules.rs +++ b/tests/ui/macros/module-path-in-nested-modules.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ run-pass mod foo { @@ -11,6 +13,6 @@ mod foo { } fn main() { - assert_eq!(module_path!(), "issue_18859"); - assert_eq!(foo::bar::baz::name(), "issue_18859::foo::bar::baz"); + assert_eq!(module_path!(), "module_path_in_nested_modules"); + assert_eq!(foo::bar::baz::name(), "module_path_in_nested_modules::foo::bar::baz"); } diff --git a/tests/ui/traits/object/trait-object-with-send-supertrait.rs b/tests/ui/traits/object/trait-object-with-send-supertrait.rs index 9dffe5640809a..cbdc8693708c9 100644 --- a/tests/ui/traits/object/trait-object-with-send-supertrait.rs +++ b/tests/ui/traits/object/trait-object-with-send-supertrait.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass #![allow(dead_code)] pub trait Foo : Send { } diff --git a/tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs b/tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs index 5cb1f9c43718e..06cd968caff1e 100644 --- a/tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs +++ b/tests/ui/unboxed-closures/auxiliary/cross-crate-generic-fn-in-closure.rs @@ -1,3 +1,5 @@ +//! Auxiliary crate for . + #![crate_type = "rlib"] pub fn inner(f: F) -> F { diff --git a/tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs b/tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs index 1d5e3349a6d42..5c943721103e6 100644 --- a/tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs +++ b/tests/ui/unboxed-closures/cross-crate-generic-fn-in-closure.rs @@ -1,10 +1,11 @@ -//@ run-pass -// Test that we don't panic on a RefCell borrow conflict in certain -// code paths involving unboxed closures. +//! Regression test for . +//! Test that we don't panic on a RefCell borrow conflict in certain +//! code paths involving unboxed closures. +//@ run-pass -//@ aux-build:issue-18711.rs -extern crate issue_18711 as issue; +//@ aux-build:cross-crate-generic-fn-in-closure.rs +extern crate cross_crate_generic_fn_in_closure as issue; fn main() { (|| issue::inner(()))(); diff --git a/tests/ui/where-clauses/impl-method-where-clause-resolution.rs b/tests/ui/where-clauses/impl-method-where-clause-resolution.rs index 84b0f5a178825..af2bf63ad106a 100644 --- a/tests/ui/where-clauses/impl-method-where-clause-resolution.rs +++ b/tests/ui/where-clauses/impl-method-where-clause-resolution.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass #![allow(dead_code)] From 9cef6416816485625457d62f494ff77bd254dc80 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 Jun 2026 16:05:47 +0200 Subject: [PATCH 8/8] Fix ICE when wrong intra-doc link on type alias --- .../passes/collect_intra_doc_links.rs | 7 ++++++- tests/rustdoc-ui/intra-doc/ty-alias-field.rs | 11 ++++++++++ .../intra-doc/ty-alias-field.stderr | 20 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-ui/intra-doc/ty-alias-field.rs create mode 100644 tests/rustdoc-ui/intra-doc/ty-alias-field.stderr diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ed0883ef76e71..68dceb103037e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -328,7 +328,12 @@ impl<'tcx> LinkCollector<'_, 'tcx> { }) } } - _ => unreachable!(), + _ => Err(UnresolvedPath { + item_id, + module_id, + partial_res: Some(Res::Def(DefKind::TyAlias, did)), + unresolved: variant_name.to_string().into(), + }), } } _ => Err(UnresolvedPath { diff --git a/tests/rustdoc-ui/intra-doc/ty-alias-field.rs b/tests/rustdoc-ui/intra-doc/ty-alias-field.rs new file mode 100644 index 0000000000000..85701ff13c021 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/ty-alias-field.rs @@ -0,0 +1,11 @@ +#![deny(rustdoc::broken_intra_doc_links)] +//~^ NOTE the lint level is defined here + +/// [Self::a::b] +//~^ ERROR unresolved link to `Self::a::b` +//~| NOTE the struct `MyStruct` has no field or associated item named `a` +pub struct MyStruct; +/// [Self::a::b] +//~^ ERROR unresolved link to `Self::a::b` +//~| NOTE the type alias `MyAlias` has no associated item named `a` +pub type MyAlias = MyStruct; diff --git a/tests/rustdoc-ui/intra-doc/ty-alias-field.stderr b/tests/rustdoc-ui/intra-doc/ty-alias-field.stderr new file mode 100644 index 0000000000000..82b38a154dcd4 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/ty-alias-field.stderr @@ -0,0 +1,20 @@ +error: unresolved link to `Self::a::b` + --> $DIR/ty-alias-field.rs:4:6 + | +LL | /// [Self::a::b] + | ^^^^^^^^^^ the struct `MyStruct` has no field or associated item named `a` + | +note: the lint level is defined here + --> $DIR/ty-alias-field.rs:1:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `Self::a::b` + --> $DIR/ty-alias-field.rs:8:6 + | +LL | /// [Self::a::b] + | ^^^^^^^^^^ the type alias `MyAlias` has no associated item named `a` + +error: aborting due to 2 previous errors +