From 9697363643da9b234ba4db0a1dd64daaf71b2880 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 26 Jan 2026 17:18:32 +0530 Subject: [PATCH 01/36] Added codeview annotation intrinsic --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 7 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 36 +++++++++++++++- compiler/rustc_codegen_ssa/src/mir/block.rs | 29 ++++++++++++- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 5 +++ .../rustc_hir_analysis/src/check/intrinsic.rs | 12 ++++++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/hint.rs | 17 ++++++++ library/core/src/intrinsics/mod.rs | 22 ++++++++++ .../intrinsics/codeview_annotation.rs | 42 +++++++++++++++++++ 9 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/intrinsics/codeview_annotation.rs diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 3f1b33c73e638..f855ad33f73d9 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -389,6 +389,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::breakpoint => { unimplemented!(); } + sym::codeview_annotation => { + bug!("codeview_annotation should be handled in block.rs"); + } sym::va_arg => { unimplemented!(); } @@ -716,6 +719,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc // FIXME(antoyo): implement. self.context.new_rvalue_from_int(self.int_type, 0) } + + fn codeview_annotation(&mut self, _strings: &[&[u8]]) { + // No-op as unlike LLVM GGC has no intrinsic we can lower to + } } impl<'a, 'gcc, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cc6ecee60b0e4..487395749f554 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -37,7 +37,7 @@ use crate::declare::declare_raw_fn; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, }; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -286,6 +286,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), + sym::codeview_annotation => { + // Ideally should be codegenned here, but we get code genned + // operand (OperandRef) in this function instead of a MIR const + // that we can evaluate to get the string. Therefore as a hack + // we codegen directly in block.rs where we do have the const + bug!("codeview_annotation should be handled in block.rs"); + } sym::va_arg => { match result.layout.backend_repr { BackendRepr::Scalar(scalar) => { @@ -834,6 +841,33 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value { self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } + + /// HACK: this function does not belong in this impl at all + /// but it will do for now as we have to put it somewhere + fn codeview_annotation(&mut self, strings: &[&[u8]]) { + if !self.cx.sess().target.is_like_msvc { + return; + } + + if strings.is_empty() { + return; + } + + // Create MDStrings from strings + let md_strings: Vec<&Metadata> = + strings.iter().map(|s| self.cx.create_metadata(s)).collect(); + + // Create MDTuple from all the MDStrings + let md_tuple = unsafe { + llvm::LLVMMDNodeInContext2(self.cx.llcx, md_strings.as_ptr(), md_strings.len()) + }; + let md_value = self.cx.get_metadata_value(md_tuple); + + // Get the intrinsic via lookup + let (fn_ty, intrinsic_fn) = self.cx.get_intrinsic("llvm.codeview.annotation".into(), &[]); + + self.call(fn_ty, None, None, intrinsic_fn, &[md_value], None, None); + } } fn catch_unwind_intrinsic<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcbd7f7e7708c..81797685542a0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::{Span, Spanned}; +use rustc_span::{Span, Spanned, sym}; use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode}; use tracing::{debug, info}; @@ -941,6 +941,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } + // HACK: Ideally should be codegenned in rustc_codegen_llvm, but + // we send code genned operand (OperandRef) there which we cannot + // eval to a string. So as a hack we codegen right here where we + // do have the const + if intrinsic.name == sym::codeview_annotation { + if let Some(target) = target { + if let Some(arg) = args.first() { + // First arg is our &'static str + if let mir::Operand::Constant(constant) = &arg.node { + let const_val = self.eval_mir_constant(constant); + // Extract bytes from the ConstValue using a diagnostics method (which is a terrible hack) + if let Some(bytes) = + const_val.try_get_slice_bytes_for_diagnostics(bx.tcx()) + { + // Split by null to get individual annotation strings + let strings: Vec<&[u8]> = bytes + .split(|&b| b == 0) + .filter(|s| !s.is_empty()) + .collect(); + bx.codeview_annotation(&strings); + } + } + } + return helper.funclet_br(self, bx, target, mergeable_succ); + } + } + let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index d1e6436f6b1eb..3dc525599d0f1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -49,4 +49,9 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; + + /// Emit a CodeView annotation with the given strings. + /// The strings are passed as raw bytes that will become MDStrings in the LLVM IR. + /// On non-Windows targets or when CodeView debug info is not enabled, this is a no-op. + fn codeview_annotation(&mut self, strings: &[&[u8]]); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 47420997a509a..26bc5de4334d7 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -86,6 +86,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ceilf32 | sym::ceilf64 | sym::ceilf128 + | sym::codeview_annotation | sym::cold_path | sym::const_eval_select | sym::contract_check_ensures @@ -293,6 +294,17 @@ pub(crate) fn check_intrinsic_type( sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), + sym::codeview_annotation => { + // codeview_annotation(s: &'static str) -> () + // Takes a string literal with null-separated substrings that become an MDTuple of MDStrings + // So the sig is: 0 type params, 0 const params, 1 input (&'static str) and returns unit + ( + 0, + 0, + vec![Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_)], + tcx.types.unit, + ) + } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 738c9b975fd00..d8acbfcf92204 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -618,6 +618,7 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, + codeview_annotation, coerce_pointee_validated, coerce_shared, coerce_unsized, diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index a8e01e6e78b4b..b1ef5c03565c3 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1025,3 +1025,20 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), } } +/// Emits a CodeView annotation with one or more labels that will appear in +/// PDB debug info on Windows. This is similar to MSVC's `__annotation()` +/// intrinsic. +/// +/// On non-Windows platforms, this macro is a no-op. +#[unstable(feature = "codeview_annotation", issue = "none")] +#[macro_export] +macro_rules! codeview_annotation { + ($($msg:literal),+ $(,)?) => {{ + // Join strings with null character separator + $crate::intrinsics::codeview_annotation($crate::concat!($($msg, "\0"),+)) + }}; +} + +#[unstable(feature = "codeview_annotation", issue = "none")] +#[doc(inline)] +pub use codeview_annotation; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 128df654e078d..7d26eb2e45fd3 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -336,6 +336,28 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); +/// Emits a CodeView annotation that will appear in PDB debug info on Windows. +/// +/// It is equivalent to MSVC's `__annotation()` intrinsic. +/// +/// It takes a string literal containing embedded annotation strings that +/// are NUL-char separated. These strings are lowered to MDTuple of MDStrings +/// in the generated LLVM IR. The string argument must be a literal. +/// +/// Using a single string argument with embedded annotation strings is a terrible +/// hack. What we need instead is &[&str] as the argument. However it is a bit more +/// involved to lower to LLVM IR. So for now we go with this approach for now. +/// +/// Use the `codeview_annotation!` macro for a more ergonomic API that takes +/// individual annotation strings and handles the NUL-separator encoding +/// automatically. +/// +/// On non-Windows platforms this intrinsic is a no-op. +#[unstable(feature = "codeview_annotation", issue = "none")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn codeview_annotation(_annotation: &'static str); + /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs new file mode 100644 index 0000000000000..904c1e25f3f9f --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -0,0 +1,42 @@ +//@ only-windows +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// CHECK-LABEL: @intrinsic_single_annotation +// CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) +#[no_mangle] +pub fn intrinsic_single_annotation() { + codeview_annotation("test_annotation"); +} + +// CHECK-LABEL: @intrinsic_multiple_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) +#[no_mangle] +pub fn intrinsic_multiple_annotations() { + codeview_annotation("category\0subcategory\0details"); +} + +// CHECK-LABEL: @macro_single_annotation +// CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_SINGLE:![0-9]+]]) +#[no_mangle] +pub fn macro_single_annotation() { + std::hint::codeview_annotation!("macro_test"); +} + +// CHECK-LABEL: @macro_multiple_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_MULTI:![0-9]+]]) +#[no_mangle] +pub fn macro_multiple_annotations() { + std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); +} + +// Metadata definitions are at the end of LLVM IR, so check them here +// CHECK-DAG: [[SINGLE]] = !{!"test_annotation"} +// CHECK-DAG: [[MULTI]] = !{!"category", !"subcategory", !"details"} +// CHECK-DAG: [[MACRO_SINGLE]] = !{!"macro_test"} +// CHECK-DAG: [[MACRO_MULTI]] = !{!"Performance", !"HotPath", !"Critical"} From 3bc1e586a4fb31e43e704ff21988393b473a51b0 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 26 Mar 2026 18:52:05 +0530 Subject: [PATCH 02/36] Changed codeview annotation to take an array instead of a string --- compiler/rustc_codegen_ssa/src/mir/block.rs | 119 +++++++++++++++--- .../rustc_hir_analysis/src/check/intrinsic.rs | 26 ++-- library/core/src/hint.rs | 3 +- library/core/src/intrinsics/mod.rs | 16 +-- .../intrinsics/codeview_annotation.rs | 4 +- 5 files changed, 124 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 81797685542a0..635ce868e6e61 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -9,7 +9,7 @@ use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::{Span, Spanned, sym}; @@ -941,27 +941,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - // HACK: Ideally should be codegenned in rustc_codegen_llvm, but - // we send code genned operand (OperandRef) there which we cannot - // eval to a string. So as a hack we codegen right here where we - // do have the const + // Emit a CodeView annotation from a [&str; N] array constant. + // We extract individual string values from the MIR constant + // and pass them to the backend for LLVM metadata generation. if intrinsic.name == sym::codeview_annotation { if let Some(target) = target { if let Some(arg) = args.first() { - // First arg is our &'static str - if let mir::Operand::Constant(constant) = &arg.node { - let const_val = self.eval_mir_constant(constant); - // Extract bytes from the ConstValue using a diagnostics method (which is a terrible hack) - if let Some(bytes) = - const_val.try_get_slice_bytes_for_diagnostics(bx.tcx()) - { - // Split by null to get individual annotation strings - let strings: Vec<&[u8]> = bytes - .split(|&b| b == 0) - .filter(|s| !s.is_empty()) - .collect(); - bx.codeview_annotation(&strings); - } + let strings = + self.extract_str_array_from_operand(bx.tcx(), &arg.node); + if !strings.is_empty() { + bx.codeview_annotation(&strings); } } return helper.funclet_br(self, bx, target, mergeable_succ); @@ -1671,6 +1660,98 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + /// Extracts string values from a `[&str; N]` MIR operand. + /// + /// Tries two strategies: + /// 1. If the operand is a constant, evaluates it and destructures the array + /// using the const evaluator. + /// 2. If the operand is a Move/Copy of a local, scans the MIR for the + /// Aggregate rvalue that constructed the array and extracts each element. + fn extract_str_array_from_operand<'b>( + &self, + tcx: TyCtxt<'tcx>, + operand: &mir::Operand<'tcx>, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + match operand { + mir::Operand::Constant(constant) => { + let const_val = self.eval_mir_constant(constant); + let array_ty = self.monomorphize(constant.ty()); + self.extract_str_array_from_const(tcx, const_val, array_ty) + } + mir::Operand::Copy(place) | mir::Operand::Move(place) => { + // Scan MIR for the Aggregate rvalue that defines this local + self.extract_str_array_from_aggregate(tcx, place.local) + } + _ => Vec::new(), + } + } + + /// Extracts strings from a `ConstValue` representing `[&str; N]`. + fn extract_str_array_from_const<'b>( + &self, + tcx: TyCtxt<'tcx>, + const_val: mir::ConstValue, + array_ty: Ty<'tcx>, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + let Some(destructured) = + tcx.try_destructure_mir_constant_for_user_output(const_val, array_ty) + else { + return Vec::new(); + }; + + destructured + .fields + .iter() + .filter_map(|(field_val, _field_ty)| field_val.try_get_slice_bytes_for_diagnostics(tcx)) + .collect() + } + + /// Fallback: extracts strings from the Aggregate rvalue that constructed + /// the array, when the operand was not folded into a constant. + fn extract_str_array_from_aggregate<'b>( + &self, + tcx: TyCtxt<'tcx>, + local: mir::Local, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + for bb_data in self.mir.basic_blocks.iter() { + for stmt in &bb_data.statements { + if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { + if place.local == local && place.projection.is_empty() { + if let mir::Rvalue::Aggregate(_, operands) = rvalue { + return operands + .iter() + .filter_map(|op| { + if let mir::Operand::Constant(c) = op { + let val = self.eval_mir_constant(c); + val.try_get_slice_bytes_for_diagnostics(tcx) + } else { + None + } + }) + .collect(); + } + // Check if it was assigned from a constant (e.g. after const prop) + if let mir::Rvalue::Use(mir::Operand::Constant(c)) = rvalue { + let val = self.eval_mir_constant(c); + let ty = self.monomorphize(c.ty()); + return self.extract_str_array_from_const(tcx, val, ty); + } + } + } + } + } + Vec::new() + } + fn codegen_argument( &mut self, bx: &mut Bx, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 26bc5de4334d7..da2092f0e8fe7 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -295,15 +295,23 @@ pub(crate) fn check_intrinsic_type( sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::codeview_annotation => { - // codeview_annotation(s: &'static str) -> () - // Takes a string literal with null-separated substrings that become an MDTuple of MDStrings - // So the sig is: 0 type params, 0 const params, 1 input (&'static str) and returns unit - ( - 0, - 0, - vec![Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_)], - tcx.types.unit, - ) + // codeview_annotation::(annotations: [&str; N]) -> () + // 0 type params, 1 const param (N: usize), array of &str, returns unit + let generics = tcx.generics_of(intrinsic_id); + let n_const = if let &ty::GenericParamDef { + name, + index, + kind: ty::GenericParamDefKind::Const { .. }, + .. + } = generics.param_at(0, tcx) + { + ty::Const::new_param(tcx, ty::ParamConst::new(index, name)) + } else { + ty::Const::new_error_with_message(tcx, span, "expected const param") + }; + let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); + let array_ty = Ty::new_array_with_const_len(tcx, str_ref, n_const); + (0, 1, vec![array_ty], tcx.types.unit) } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index b1ef5c03565c3..ec8ba3666bdd7 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1034,8 +1034,7 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { #[macro_export] macro_rules! codeview_annotation { ($($msg:literal),+ $(,)?) => {{ - // Join strings with null character separator - $crate::intrinsics::codeview_annotation($crate::concat!($($msg, "\0"),+)) + $crate::intrinsics::codeview_annotation([$($msg),+]) }}; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7d26eb2e45fd3..e05d4ce5d79c7 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -340,23 +340,15 @@ pub fn breakpoint(); /// /// It is equivalent to MSVC's `__annotation()` intrinsic. /// -/// It takes a string literal containing embedded annotation strings that -/// are NUL-char separated. These strings are lowered to MDTuple of MDStrings -/// in the generated LLVM IR. The string argument must be a literal. -/// -/// Using a single string argument with embedded annotation strings is a terrible -/// hack. What we need instead is &[&str] as the argument. However it is a bit more -/// involved to lower to LLVM IR. So for now we go with this approach for now. -/// -/// Use the `codeview_annotation!` macro for a more ergonomic API that takes -/// individual annotation strings and handles the NUL-separator encoding -/// automatically. +/// Takes an array of string literal annotations that are lowered to an +/// MDTuple of MDStrings in the generated LLVM IR. All strings must be +/// compile-time constants. /// /// On non-Windows platforms this intrinsic is a no-op. #[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_annotation: &'static str); +pub fn codeview_annotation(_annotations: [&'static str; N]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index 904c1e25f3f9f..f684ad220be3b 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -11,14 +11,14 @@ use std::intrinsics::codeview_annotation; // CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) #[no_mangle] pub fn intrinsic_single_annotation() { - codeview_annotation("test_annotation"); + codeview_annotation(["test_annotation"]); } // CHECK-LABEL: @intrinsic_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) #[no_mangle] pub fn intrinsic_multiple_annotations() { - codeview_annotation("category\0subcategory\0details"); + codeview_annotation(["category", "subcategory", "details"]); } // CHECK-LABEL: @macro_single_annotation From 35b35a49480d60d51803c9944da8610de7638b51 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sat, 28 Mar 2026 17:50:49 +0530 Subject: [PATCH 03/36] Changed intrinsic arg from array to array ref --- compiler/rustc_codegen_ssa/src/mir/block.rs | 74 ++++++++++++------- .../rustc_hir_analysis/src/check/intrinsic.rs | 3 +- library/core/src/hint.rs | 2 +- library/core/src/intrinsics/mod.rs | 2 +- .../intrinsics/codeview_annotation.rs | 4 +- 5 files changed, 55 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 635ce868e6e61..1460ae425e659 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -941,7 +941,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - // Emit a CodeView annotation from a [&str; N] array constant. + // Emit a CodeView annotation from a &[&str; N] array reference constant. // We extract individual string values from the MIR constant // and pass them to the backend for LLVM metadata generation. if intrinsic.name == sym::codeview_annotation { @@ -1660,13 +1660,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - /// Extracts string values from a `[&str; N]` MIR operand. + /// Extracts string values from a `&[&str; N]` MIR operand. /// /// Tries two strategies: /// 1. If the operand is a constant, evaluates it and destructures the array /// using the const evaluator. /// 2. If the operand is a Move/Copy of a local, scans the MIR for the - /// Aggregate rvalue that constructed the array and extracts each element. + /// constant assignment that defines it. fn extract_str_array_from_operand<'b>( &self, tcx: TyCtxt<'tcx>, @@ -1678,17 +1678,55 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match operand { mir::Operand::Constant(constant) => { let const_val = self.eval_mir_constant(constant); - let array_ty = self.monomorphize(constant.ty()); - self.extract_str_array_from_const(tcx, const_val, array_ty) + let ref_ty = self.monomorphize(constant.ty()); + self.extract_str_array_from_ref_const(tcx, const_val, ref_ty) } mir::Operand::Copy(place) | mir::Operand::Move(place) => { - // Scan MIR for the Aggregate rvalue that defines this local - self.extract_str_array_from_aggregate(tcx, place.local) + self.extract_str_array_from_local(tcx, place.local) } _ => Vec::new(), } } + /// Extracts strings from a `ConstValue` representing `&[&str; N]`. + /// + /// The reference is a thin pointer (`ConstValue::Scalar(Ptr(...))`). + /// We dereference it to get the inner `[&str; N]` array, then destructure + /// that array into individual `&str` fields. + fn extract_str_array_from_ref_const<'b>( + &self, + tcx: TyCtxt<'tcx>, + const_val: mir::ConstValue, + ref_ty: Ty<'tcx>, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + // Peel the reference to get the inner array type + let array_ty = match ref_ty.kind() { + ty::Ref(_, inner_ty, _) => *inner_ty, + _ => return Vec::new(), + }; + + // The reference is a thin pointer — extract alloc_id and offset + let array_const = match const_val { + mir::ConstValue::Scalar(scalar) => { + let Some(ptr) = scalar.to_pointer(&tcx).discard_err() else { + return Vec::new(); + }; + let Some((prov, offset)) = + ptr.into_pointer_or_addr().ok().map(|p| p.prov_and_relative_offset()) + else { + return Vec::new(); + }; + mir::ConstValue::Indirect { alloc_id: prov.alloc_id(), offset } + } + _ => return Vec::new(), + }; + + self.extract_str_array_from_const(tcx, array_const, array_ty) + } + /// Extracts strings from a `ConstValue` representing `[&str; N]`. fn extract_str_array_from_const<'b>( &self, @@ -1712,9 +1750,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .collect() } - /// Fallback: extracts strings from the Aggregate rvalue that constructed - /// the array, when the operand was not folded into a constant. - fn extract_str_array_from_aggregate<'b>( + /// Fallback: extracts strings when the operand is a Move/Copy of a local. + /// Scans the MIR for the constant assignment that defines the local. + fn extract_str_array_from_local<'b>( &self, tcx: TyCtxt<'tcx>, local: mir::Local, @@ -1726,24 +1764,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for stmt in &bb_data.statements { if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { if place.local == local && place.projection.is_empty() { - if let mir::Rvalue::Aggregate(_, operands) = rvalue { - return operands - .iter() - .filter_map(|op| { - if let mir::Operand::Constant(c) = op { - let val = self.eval_mir_constant(c); - val.try_get_slice_bytes_for_diagnostics(tcx) - } else { - None - } - }) - .collect(); - } - // Check if it was assigned from a constant (e.g. after const prop) if let mir::Rvalue::Use(mir::Operand::Constant(c)) = rvalue { let val = self.eval_mir_constant(c); let ty = self.monomorphize(c.ty()); - return self.extract_str_array_from_const(tcx, val, ty); + return self.extract_str_array_from_ref_const(tcx, val, ty); } } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index da2092f0e8fe7..1866961608e3d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -311,7 +311,8 @@ pub(crate) fn check_intrinsic_type( }; let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); let array_ty = Ty::new_array_with_const_len(tcx, str_ref, n_const); - (0, 1, vec![array_ty], tcx.types.unit) + let ref_to_array = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, array_ty); + (0, 1, vec![ref_to_array], tcx.types.unit) } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index ec8ba3666bdd7..6fd389288883d 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1034,7 +1034,7 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { #[macro_export] macro_rules! codeview_annotation { ($($msg:literal),+ $(,)?) => {{ - $crate::intrinsics::codeview_annotation([$($msg),+]) + $crate::intrinsics::codeview_annotation(&[$($msg),+]) }}; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index e05d4ce5d79c7..7ccb03df317b0 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -348,7 +348,7 @@ pub fn breakpoint(); #[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_annotations: [&'static str; N]); +pub fn codeview_annotation(_annotations: &'static [&'static str; N]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index f684ad220be3b..13cf423c87004 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -11,14 +11,14 @@ use std::intrinsics::codeview_annotation; // CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) #[no_mangle] pub fn intrinsic_single_annotation() { - codeview_annotation(["test_annotation"]); + codeview_annotation(&["test_annotation"]); } // CHECK-LABEL: @intrinsic_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) #[no_mangle] pub fn intrinsic_multiple_annotations() { - codeview_annotation(["category", "subcategory", "details"]); + codeview_annotation(&["category", "subcategory", "details"]); } // CHECK-LABEL: @macro_single_annotation From 501ab33f82c8d645595095f5c614af0ab55066be Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sat, 28 Mar 2026 18:50:05 +0530 Subject: [PATCH 04/36] Changed intrinsic arg from array ref to slice --- compiler/rustc_codegen_ssa/src/mir/block.rs | 186 ++++++++++++++---- .../rustc_hir_analysis/src/check/intrinsic.rs | 22 +-- library/core/src/intrinsics/mod.rs | 4 +- 3 files changed, 157 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1460ae425e659..2951815ee50de 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -941,14 +941,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - // Emit a CodeView annotation from a &[&str; N] array reference constant. + // Emit a CodeView annotation from a &[&str] slice constant. // We extract individual string values from the MIR constant // and pass them to the backend for LLVM metadata generation. if intrinsic.name == sym::codeview_annotation { if let Some(target) = target { if let Some(arg) = args.first() { let strings = - self.extract_str_array_from_operand(bx.tcx(), &arg.node); + self.extract_str_slice_from_operand(bx.tcx(), &arg.node); if !strings.is_empty() { bx.codeview_annotation(&strings); } @@ -1660,14 +1660,84 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - /// Extracts string values from a `&[&str; N]` MIR operand. + /// Extracts string values from a `&[&str]` MIR operand. /// - /// Tries two strategies: - /// 1. If the operand is a constant, evaluates it and destructures the array - /// using the const evaluator. - /// 2. If the operand is a Move/Copy of a local, scans the MIR for the - /// constant assignment that defines it. - fn extract_str_array_from_operand<'b>( + /// The operand is typically a Move/Copy of a local that was assigned + /// via an unsizing coercion (`Cast(Unsize)`) from `&[&str; N]`. + /// We trace through the MIR to find the source constant. + fn extract_str_slice_from_operand<'b>( + &self, + tcx: TyCtxt<'tcx>, + operand: &mir::Operand<'tcx>, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + match operand { + mir::Operand::Constant(constant) => { + // If somehow the slice ref is directly a constant + let const_val = self.eval_mir_constant(constant); + let ref_ty = self.monomorphize(constant.ty()); + self.extract_str_slice_from_wide_ptr(tcx, const_val, ref_ty) + } + mir::Operand::Copy(place) | mir::Operand::Move(place) => { + // Trace through MIR to find the source constant + self.extract_str_slice_from_local(tcx, place.local) + } + _ => Vec::new(), + } + } + + /// Traces a local back through MIR assignments to find the source + /// constant for a `&[&str]` slice reference. + /// + /// Handles two patterns: + /// 1. `_local = move _src as &[&str] (PointerCoercion(Unsize))` — unsizing cast + /// 2. `_local = const ...` — direct constant assignment + fn extract_str_slice_from_local<'b>( + &self, + tcx: TyCtxt<'tcx>, + local: mir::Local, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + for bb_data in self.mir.basic_blocks.iter() { + for stmt in &bb_data.statements { + if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { + if place.local != local || !place.projection.is_empty() { + continue; + } + match rvalue { + // Unsizing cast: &[&str; N] -> &[&str] + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion( + ty::adjustment::PointerCoercion::Unsize, + _, + ), + source_operand, + _cast_ty, + ) => { + // The source is &[&str; N] — extract strings from it + return self.extract_str_array_from_source(tcx, source_operand); + } + // Direct constant assignment + mir::Rvalue::Use(mir::Operand::Constant(c)) => { + let val = self.eval_mir_constant(c); + let ty = self.monomorphize(c.ty()); + return self.extract_str_slice_from_wide_ptr(tcx, val, ty); + } + _ => {} + } + } + } + } + Vec::new() + } + + /// Extracts strings from the source operand of an unsizing cast. + /// The source is `&[&str; N]` (a thin pointer to a fixed-size array). + fn extract_str_array_from_source<'b>( &self, tcx: TyCtxt<'tcx>, operand: &mir::Operand<'tcx>, @@ -1682,12 +1752,38 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.extract_str_array_from_ref_const(tcx, const_val, ref_ty) } mir::Operand::Copy(place) | mir::Operand::Move(place) => { - self.extract_str_array_from_local(tcx, place.local) + // The source might itself be assigned from a promoted constant + self.extract_str_array_source_from_local(tcx, place.local) } _ => Vec::new(), } } + /// Traces a local that holds `&[&str; N]` back to its constant definition. + fn extract_str_array_source_from_local<'b>( + &self, + tcx: TyCtxt<'tcx>, + local: mir::Local, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + for bb_data in self.mir.basic_blocks.iter() { + for stmt in &bb_data.statements { + if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { + if place.local == local && place.projection.is_empty() { + if let mir::Rvalue::Use(mir::Operand::Constant(c)) = rvalue { + let val = self.eval_mir_constant(c); + let ty = self.monomorphize(c.ty()); + return self.extract_str_array_from_ref_const(tcx, val, ty); + } + } + } + } + } + Vec::new() + } + /// Extracts strings from a `ConstValue` representing `&[&str; N]`. /// /// The reference is a thin pointer (`ConstValue::Scalar(Ptr(...))`). @@ -1727,6 +1823,50 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.extract_str_array_from_const(tcx, array_const, array_ty) } + /// Extracts strings from a `ConstValue` representing `&[&str]` (wide pointer). + fn extract_str_slice_from_wide_ptr<'b>( + &self, + tcx: TyCtxt<'tcx>, + const_val: mir::ConstValue, + ref_ty: Ty<'tcx>, + ) -> Vec<&'b [u8]> + where + 'tcx: 'b, + { + // Verify this is &[&str] + let inner_ty = match ref_ty.kind() { + ty::Ref(_, inner_ty, _) => *inner_ty, + _ => return Vec::new(), + }; + if !matches!(inner_ty.kind(), ty::Slice(_)) { + return Vec::new(); + } + + // Wide pointer: ConstValue::Slice { alloc_id, meta } or ConstValue::Indirect + // The alloc contains N × (ptr, len) pairs for each &str element. + // We can construct an array type with the known length and destructure it. + let len = match const_val { + mir::ConstValue::Slice { meta, .. } => meta, + _ => return Vec::new(), + }; + + // Build [&str; len] type and Indirect const for the slice data + let elem_ty = match inner_ty.kind() { + ty::Slice(elem) => *elem, + _ => return Vec::new(), + }; + let array_ty = Ty::new_array(tcx, elem_ty, len); + + let array_const = match const_val { + mir::ConstValue::Slice { alloc_id, .. } => { + mir::ConstValue::Indirect { alloc_id, offset: rustc_abi::Size::ZERO } + } + _ => return Vec::new(), + }; + + self.extract_str_array_from_const(tcx, array_const, array_ty) + } + /// Extracts strings from a `ConstValue` representing `[&str; N]`. fn extract_str_array_from_const<'b>( &self, @@ -1750,32 +1890,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .collect() } - /// Fallback: extracts strings when the operand is a Move/Copy of a local. - /// Scans the MIR for the constant assignment that defines the local. - fn extract_str_array_from_local<'b>( - &self, - tcx: TyCtxt<'tcx>, - local: mir::Local, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - for bb_data in self.mir.basic_blocks.iter() { - for stmt in &bb_data.statements { - if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if place.local == local && place.projection.is_empty() { - if let mir::Rvalue::Use(mir::Operand::Constant(c)) = rvalue { - let val = self.eval_mir_constant(c); - let ty = self.monomorphize(c.ty()); - return self.extract_str_array_from_ref_const(tcx, val, ty); - } - } - } - } - } - Vec::new() - } - fn codegen_argument( &mut self, bx: &mut Bx, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 1866961608e3d..c6024eedb258a 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -295,24 +295,12 @@ pub(crate) fn check_intrinsic_type( sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::codeview_annotation => { - // codeview_annotation::(annotations: [&str; N]) -> () - // 0 type params, 1 const param (N: usize), array of &str, returns unit - let generics = tcx.generics_of(intrinsic_id); - let n_const = if let &ty::GenericParamDef { - name, - index, - kind: ty::GenericParamDefKind::Const { .. }, - .. - } = generics.param_at(0, tcx) - { - ty::Const::new_param(tcx, ty::ParamConst::new(index, name)) - } else { - ty::Const::new_error_with_message(tcx, span, "expected const param") - }; + // codeview_annotation(annotations: &'static [&'static str]) -> () + // 0 type params, 0 const params, 1 input (&'static [&'static str]), returns unit let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); - let array_ty = Ty::new_array_with_const_len(tcx, str_ref, n_const); - let ref_to_array = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, array_ty); - (0, 1, vec![ref_to_array], tcx.types.unit) + let slice_ty = Ty::new_slice(tcx, str_ref); + let ref_to_slice = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, slice_ty); + (0, 0, vec![ref_to_slice], tcx.types.unit) } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7ccb03df317b0..180068c857354 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -340,7 +340,7 @@ pub fn breakpoint(); /// /// It is equivalent to MSVC's `__annotation()` intrinsic. /// -/// Takes an array of string literal annotations that are lowered to an +/// Takes a slice of string literal annotations that are lowered to an /// MDTuple of MDStrings in the generated LLVM IR. All strings must be /// compile-time constants. /// @@ -348,7 +348,7 @@ pub fn breakpoint(); #[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_annotations: &'static [&'static str; N]); +pub fn codeview_annotation(_annotations: &'static [&'static str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. From 84d9cbfb8e0b49a9bbf76db1861eab7ad950629f Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 30 Mar 2026 16:26:15 +0530 Subject: [PATCH 05/36] Added tests to verify annotation survives optimizations --- .../codeview_annotation_optimized.rs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs new file mode 100644 index 0000000000000..e61dd75da1b21 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs @@ -0,0 +1,51 @@ +//@ only-windows +//@ compile-flags: -Copt-level=2 +// +// Verify that codeview annotations survive LLVM optimizations. +// The llvm.codeview.annotation intrinsic is marked noduplicate and +// writes to inaccessible memory, so LLVM must not remove it. + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// Verify that an annotation inside a function with other code is not +// removed even when surrounding code is optimized. +// CHECK-LABEL: @annotation_with_computation +// CHECK: call void @llvm.codeview.annotation(metadata [[COMP:![0-9]+]]) +#[no_mangle] +pub fn annotation_with_computation(x: u32) -> u32 { + codeview_annotation(&["in_computation"]); + x.wrapping_mul(31).wrapping_add(17) +} + +// Verify that multiple annotations in the same function are all preserved. +// CHECK-LABEL: @multiple_annotations_same_fn +// CHECK: call void @llvm.codeview.annotation(metadata [[FIRST:![0-9]+]]) +// CHECK: call void @llvm.codeview.annotation(metadata [[SECOND:![0-9]+]]) +#[no_mangle] +pub fn multiple_annotations_same_fn() { + codeview_annotation(&["first"]); + codeview_annotation(&["second"]); +} + +// Verify that annotations survive inlining: the annotation from +// the inlined callee should appear in the caller. +// CHECK-LABEL: @annotation_after_inlining +// CHECK: call void @llvm.codeview.annotation(metadata [[INLINED:![0-9]+]]) +#[no_mangle] +pub fn annotation_after_inlining() { + annotated_helper(); +} + +#[inline(always)] +fn annotated_helper() { + codeview_annotation(&["from_inlined_fn"]); +} + +// CHECK-DAG: [[COMP]] = !{!"in_computation"} +// CHECK-DAG: [[FIRST]] = !{!"first"} +// CHECK-DAG: [[SECOND]] = !{!"second"} +// CHECK-DAG: [[INLINED]] = !{!"from_inlined_fn"} From 14dcac307fef9356a39d229706db70b6c52c952c Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Wed, 8 Apr 2026 09:22:35 +0530 Subject: [PATCH 06/36] Renamed codeview_annotation to debug_annotation --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 6 +++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 6 +++--- compiler/rustc_codegen_ssa/src/mir/block.rs | 6 +++--- compiler/rustc_codegen_ssa/src/traits/intrinsic.rs | 6 +++--- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 6 +++--- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/hint.rs | 12 ++++++------ library/core/src/intrinsics/mod.rs | 6 +++--- ...{codeview_annotation.rs => debug_annotation.rs} | 12 ++++++------ ..._optimized.rs => debug_annotation_optimized.rs} | 14 +++++++------- 10 files changed, 38 insertions(+), 38 deletions(-) rename tests/codegen-llvm/intrinsics/{codeview_annotation.rs => debug_annotation.rs} (78%) rename tests/codegen-llvm/intrinsics/{codeview_annotation_optimized.rs => debug_annotation_optimized.rs} (82%) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index f855ad33f73d9..dfe0abcff60aa 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -389,8 +389,8 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::breakpoint => { unimplemented!(); } - sym::codeview_annotation => { - bug!("codeview_annotation should be handled in block.rs"); + sym::debug_annotation => { + bug!("debug_annotation should be handled in block.rs"); } sym::va_arg => { unimplemented!(); @@ -720,7 +720,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn codeview_annotation(&mut self, _strings: &[&[u8]]) { + fn debug_annotation(&mut self, _strings: &[&[u8]]) { // No-op as unlike LLVM GGC has no intrinsic we can lower to } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 487395749f554..275c79f578872 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -286,12 +286,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), - sym::codeview_annotation => { + sym::debug_annotation => { // Ideally should be codegenned here, but we get code genned // operand (OperandRef) in this function instead of a MIR const // that we can evaluate to get the string. Therefore as a hack // we codegen directly in block.rs where we do have the const - bug!("codeview_annotation should be handled in block.rs"); + bug!("debug_annotation should be handled in block.rs"); } sym::va_arg => { match result.layout.backend_repr { @@ -844,7 +844,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { /// HACK: this function does not belong in this impl at all /// but it will do for now as we have to put it somewhere - fn codeview_annotation(&mut self, strings: &[&[u8]]) { + fn debug_annotation(&mut self, strings: &[&[u8]]) { if !self.cx.sess().target.is_like_msvc { return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 2951815ee50de..8d28d89f0006f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -941,16 +941,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - // Emit a CodeView annotation from a &[&str] slice constant. + // Emit a debug annotation from a &[&str] slice constant. // We extract individual string values from the MIR constant // and pass them to the backend for LLVM metadata generation. - if intrinsic.name == sym::codeview_annotation { + if intrinsic.name == sym::debug_annotation { if let Some(target) = target { if let Some(arg) = args.first() { let strings = self.extract_str_slice_from_operand(bx.tcx(), &arg.node); if !strings.is_empty() { - bx.codeview_annotation(&strings); + bx.debug_annotation(&strings); } } return helper.funclet_br(self, bx, target, mergeable_succ); diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 3dc525599d0f1..f68d063bb475f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -50,8 +50,8 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; - /// Emit a CodeView annotation with the given strings. + /// Emit a debug annotation with the given strings. /// The strings are passed as raw bytes that will become MDStrings in the LLVM IR. - /// On non-Windows targets or when CodeView debug info is not enabled, this is a no-op. - fn codeview_annotation(&mut self, strings: &[&[u8]]); + /// On non-Windows targets or when debug info is not enabled, this is a no-op. + fn debug_annotation(&mut self, strings: &[&[u8]]); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index c6024eedb258a..df86eb7a50017 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -86,7 +86,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ceilf32 | sym::ceilf64 | sym::ceilf128 - | sym::codeview_annotation | sym::cold_path | sym::const_eval_select | sym::contract_check_ensures @@ -103,6 +102,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ctlz | sym::ctpop | sym::cttz + | sym::debug_annotation | sym::discriminant_value | sym::exp2f16 | sym::exp2f32 @@ -294,8 +294,8 @@ pub(crate) fn check_intrinsic_type( sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), - sym::codeview_annotation => { - // codeview_annotation(annotations: &'static [&'static str]) -> () + sym::debug_annotation => { + // debug_annotation(annotations: &'static [&'static str]) -> () // 0 type params, 0 const params, 1 input (&'static [&'static str]), returns unit let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); let slice_ty = Ty::new_slice(tcx, str_ref); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d8acbfcf92204..766214fb67784 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -618,7 +618,7 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, - codeview_annotation, + debug_annotation, coerce_pointee_validated, coerce_shared, coerce_unsized, diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6fd389288883d..6d030e0c2600e 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1025,19 +1025,19 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), } } -/// Emits a CodeView annotation with one or more labels that will appear in +/// Emits a debug annotation with one or more labels that will appear in /// PDB debug info on Windows. This is similar to MSVC's `__annotation()` /// intrinsic. /// /// On non-Windows platforms, this macro is a no-op. -#[unstable(feature = "codeview_annotation", issue = "none")] +#[unstable(feature = "debug_annotation", issue = "none")] #[macro_export] -macro_rules! codeview_annotation { +macro_rules! debug_annotation { ($($msg:literal),+ $(,)?) => {{ - $crate::intrinsics::codeview_annotation(&[$($msg),+]) + $crate::intrinsics::debug_annotation(&[$($msg),+]) }}; } -#[unstable(feature = "codeview_annotation", issue = "none")] +#[unstable(feature = "debug_annotation", issue = "none")] #[doc(inline)] -pub use codeview_annotation; +pub use debug_annotation; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 180068c857354..3bee7c80011f6 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -336,7 +336,7 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); -/// Emits a CodeView annotation that will appear in PDB debug info on Windows. +/// Emits a debug annotation that will appear in PDB debug info on Windows. /// /// It is equivalent to MSVC's `__annotation()` intrinsic. /// @@ -345,10 +345,10 @@ pub fn breakpoint(); /// compile-time constants. /// /// On non-Windows platforms this intrinsic is a no-op. -#[unstable(feature = "codeview_annotation", issue = "none")] +#[unstable(feature = "debug_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_annotations: &'static [&'static str]); +pub fn debug_annotation(_annotations: &'static [&'static str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/debug_annotation.rs similarity index 78% rename from tests/codegen-llvm/intrinsics/codeview_annotation.rs rename to tests/codegen-llvm/intrinsics/debug_annotation.rs index 13cf423c87004..b49b7f06b4975 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/debug_annotation.rs @@ -2,37 +2,37 @@ //@ compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(codeview_annotation)] +#![feature(debug_annotation)] #![feature(core_intrinsics)] -use std::intrinsics::codeview_annotation; +use std::intrinsics::debug_annotation; // CHECK-LABEL: @intrinsic_single_annotation // CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) #[no_mangle] pub fn intrinsic_single_annotation() { - codeview_annotation(&["test_annotation"]); + debug_annotation(&["test_annotation"]); } // CHECK-LABEL: @intrinsic_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) #[no_mangle] pub fn intrinsic_multiple_annotations() { - codeview_annotation(&["category", "subcategory", "details"]); + debug_annotation(&["category", "subcategory", "details"]); } // CHECK-LABEL: @macro_single_annotation // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_SINGLE:![0-9]+]]) #[no_mangle] pub fn macro_single_annotation() { - std::hint::codeview_annotation!("macro_test"); + std::hint::debug_annotation!("macro_test"); } // CHECK-LABEL: @macro_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_MULTI:![0-9]+]]) #[no_mangle] pub fn macro_multiple_annotations() { - std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); + std::hint::debug_annotation!("Performance", "HotPath", "Critical"); } // Metadata definitions are at the end of LLVM IR, so check them here diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs b/tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs similarity index 82% rename from tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs rename to tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs index e61dd75da1b21..d9d0583547e23 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs +++ b/tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs @@ -1,15 +1,15 @@ //@ only-windows //@ compile-flags: -Copt-level=2 // -// Verify that codeview annotations survive LLVM optimizations. +// Verify that debug annotations survive LLVM optimizations. // The llvm.codeview.annotation intrinsic is marked noduplicate and // writes to inaccessible memory, so LLVM must not remove it. #![crate_type = "lib"] -#![feature(codeview_annotation)] +#![feature(debug_annotation)] #![feature(core_intrinsics)] -use std::intrinsics::codeview_annotation; +use std::intrinsics::debug_annotation; // Verify that an annotation inside a function with other code is not // removed even when surrounding code is optimized. @@ -17,7 +17,7 @@ use std::intrinsics::codeview_annotation; // CHECK: call void @llvm.codeview.annotation(metadata [[COMP:![0-9]+]]) #[no_mangle] pub fn annotation_with_computation(x: u32) -> u32 { - codeview_annotation(&["in_computation"]); + debug_annotation(&["in_computation"]); x.wrapping_mul(31).wrapping_add(17) } @@ -27,8 +27,8 @@ pub fn annotation_with_computation(x: u32) -> u32 { // CHECK: call void @llvm.codeview.annotation(metadata [[SECOND:![0-9]+]]) #[no_mangle] pub fn multiple_annotations_same_fn() { - codeview_annotation(&["first"]); - codeview_annotation(&["second"]); + debug_annotation(&["first"]); + debug_annotation(&["second"]); } // Verify that annotations survive inlining: the annotation from @@ -42,7 +42,7 @@ pub fn annotation_after_inlining() { #[inline(always)] fn annotated_helper() { - codeview_annotation(&["from_inlined_fn"]); + debug_annotation(&["from_inlined_fn"]); } // CHECK-DAG: [[COMP]] = !{!"in_computation"} From 9a0aa43313ad86404b6599cfc6120e381d367ea9 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Wed, 8 Apr 2026 16:56:08 +0530 Subject: [PATCH 07/36] Now intercepting string arg while lowering to MIR --- compiler/rustc_borrowck/src/lib.rs | 3 +- .../src/polonius/legacy/loan_invalidations.rs | 1 + compiler/rustc_borrowck/src/type_check/mod.rs | 1 + compiler/rustc_codegen_ssa/src/mir/block.rs | 250 +----------------- .../rustc_codegen_ssa/src/mir/statement.rs | 6 + .../src/interpret/intrinsics.rs | 1 + compiler/rustc_middle/src/mir/pretty.rs | 10 + compiler/rustc_middle/src/mir/syntax.rs | 9 + compiler/rustc_middle/src/mir/visit.rs | 1 + .../rustc_mir_build/src/builder/expr/into.rs | 93 ++++++- .../rustc_mir_transform/src/cost_checker.rs | 1 + .../src/dataflow_const_prop.rs | 1 + .../rustc_mir_transform/src/jump_threading.rs | 1 + compiler/rustc_mir_transform/src/validate.rs | 1 + compiler/rustc_public/src/mir/body.rs | 1 + compiler/rustc_public/src/mir/visit.rs | 1 + .../src/unstable/convert/stable/mir.rs | 5 + compiler/rustc_span/src/symbol.rs | 2 +- tests/ui/intrinsics/debug_annotation.rs | 41 +++ tests/ui/intrinsics/debug_annotation.stderr | 8 + 20 files changed, 186 insertions(+), 251 deletions(-) create mode 100644 tests/ui/intrinsics/debug_annotation.rs create mode 100644 tests/ui/intrinsics/debug_annotation.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 114f9d864e735..d1ce5eb9f7ef0 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -828,7 +828,8 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( span, "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", - ) + ), + NonDivergingIntrinsic::DebugAnnotation(_) => {}, } // Only relevant for mir typeck StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 439aa1a91e068..eb9a47e6ef750 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -65,6 +65,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, dst); self.consume_operand(location, count); } + StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(_)) => {} // Only relevant for mir typeck StatementKind::AscribeUserType(..) // Only relevant for liveness and unsafeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 47f14dc3df62e..d436210442416 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -691,6 +691,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(..)) | StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8d28d89f0006f..dcbd7f7e7708c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -9,10 +9,10 @@ use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::{Span, Spanned, sym}; +use rustc_span::{Span, Spanned}; use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode}; use tracing::{debug, info}; @@ -941,22 +941,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - // Emit a debug annotation from a &[&str] slice constant. - // We extract individual string values from the MIR constant - // and pass them to the backend for LLVM metadata generation. - if intrinsic.name == sym::debug_annotation { - if let Some(target) = target { - if let Some(arg) = args.first() { - let strings = - self.extract_str_slice_from_operand(bx.tcx(), &arg.node); - if !strings.is_empty() { - bx.debug_annotation(&strings); - } - } - return helper.funclet_br(self, bx, target, mergeable_succ); - } - } - let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); @@ -1660,236 +1644,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - /// Extracts string values from a `&[&str]` MIR operand. - /// - /// The operand is typically a Move/Copy of a local that was assigned - /// via an unsizing coercion (`Cast(Unsize)`) from `&[&str; N]`. - /// We trace through the MIR to find the source constant. - fn extract_str_slice_from_operand<'b>( - &self, - tcx: TyCtxt<'tcx>, - operand: &mir::Operand<'tcx>, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - match operand { - mir::Operand::Constant(constant) => { - // If somehow the slice ref is directly a constant - let const_val = self.eval_mir_constant(constant); - let ref_ty = self.monomorphize(constant.ty()); - self.extract_str_slice_from_wide_ptr(tcx, const_val, ref_ty) - } - mir::Operand::Copy(place) | mir::Operand::Move(place) => { - // Trace through MIR to find the source constant - self.extract_str_slice_from_local(tcx, place.local) - } - _ => Vec::new(), - } - } - - /// Traces a local back through MIR assignments to find the source - /// constant for a `&[&str]` slice reference. - /// - /// Handles two patterns: - /// 1. `_local = move _src as &[&str] (PointerCoercion(Unsize))` — unsizing cast - /// 2. `_local = const ...` — direct constant assignment - fn extract_str_slice_from_local<'b>( - &self, - tcx: TyCtxt<'tcx>, - local: mir::Local, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - for bb_data in self.mir.basic_blocks.iter() { - for stmt in &bb_data.statements { - if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if place.local != local || !place.projection.is_empty() { - continue; - } - match rvalue { - // Unsizing cast: &[&str; N] -> &[&str] - mir::Rvalue::Cast( - mir::CastKind::PointerCoercion( - ty::adjustment::PointerCoercion::Unsize, - _, - ), - source_operand, - _cast_ty, - ) => { - // The source is &[&str; N] — extract strings from it - return self.extract_str_array_from_source(tcx, source_operand); - } - // Direct constant assignment - mir::Rvalue::Use(mir::Operand::Constant(c)) => { - let val = self.eval_mir_constant(c); - let ty = self.monomorphize(c.ty()); - return self.extract_str_slice_from_wide_ptr(tcx, val, ty); - } - _ => {} - } - } - } - } - Vec::new() - } - - /// Extracts strings from the source operand of an unsizing cast. - /// The source is `&[&str; N]` (a thin pointer to a fixed-size array). - fn extract_str_array_from_source<'b>( - &self, - tcx: TyCtxt<'tcx>, - operand: &mir::Operand<'tcx>, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - match operand { - mir::Operand::Constant(constant) => { - let const_val = self.eval_mir_constant(constant); - let ref_ty = self.monomorphize(constant.ty()); - self.extract_str_array_from_ref_const(tcx, const_val, ref_ty) - } - mir::Operand::Copy(place) | mir::Operand::Move(place) => { - // The source might itself be assigned from a promoted constant - self.extract_str_array_source_from_local(tcx, place.local) - } - _ => Vec::new(), - } - } - - /// Traces a local that holds `&[&str; N]` back to its constant definition. - fn extract_str_array_source_from_local<'b>( - &self, - tcx: TyCtxt<'tcx>, - local: mir::Local, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - for bb_data in self.mir.basic_blocks.iter() { - for stmt in &bb_data.statements { - if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if place.local == local && place.projection.is_empty() { - if let mir::Rvalue::Use(mir::Operand::Constant(c)) = rvalue { - let val = self.eval_mir_constant(c); - let ty = self.monomorphize(c.ty()); - return self.extract_str_array_from_ref_const(tcx, val, ty); - } - } - } - } - } - Vec::new() - } - - /// Extracts strings from a `ConstValue` representing `&[&str; N]`. - /// - /// The reference is a thin pointer (`ConstValue::Scalar(Ptr(...))`). - /// We dereference it to get the inner `[&str; N]` array, then destructure - /// that array into individual `&str` fields. - fn extract_str_array_from_ref_const<'b>( - &self, - tcx: TyCtxt<'tcx>, - const_val: mir::ConstValue, - ref_ty: Ty<'tcx>, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - // Peel the reference to get the inner array type - let array_ty = match ref_ty.kind() { - ty::Ref(_, inner_ty, _) => *inner_ty, - _ => return Vec::new(), - }; - - // The reference is a thin pointer — extract alloc_id and offset - let array_const = match const_val { - mir::ConstValue::Scalar(scalar) => { - let Some(ptr) = scalar.to_pointer(&tcx).discard_err() else { - return Vec::new(); - }; - let Some((prov, offset)) = - ptr.into_pointer_or_addr().ok().map(|p| p.prov_and_relative_offset()) - else { - return Vec::new(); - }; - mir::ConstValue::Indirect { alloc_id: prov.alloc_id(), offset } - } - _ => return Vec::new(), - }; - - self.extract_str_array_from_const(tcx, array_const, array_ty) - } - - /// Extracts strings from a `ConstValue` representing `&[&str]` (wide pointer). - fn extract_str_slice_from_wide_ptr<'b>( - &self, - tcx: TyCtxt<'tcx>, - const_val: mir::ConstValue, - ref_ty: Ty<'tcx>, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - // Verify this is &[&str] - let inner_ty = match ref_ty.kind() { - ty::Ref(_, inner_ty, _) => *inner_ty, - _ => return Vec::new(), - }; - if !matches!(inner_ty.kind(), ty::Slice(_)) { - return Vec::new(); - } - - // Wide pointer: ConstValue::Slice { alloc_id, meta } or ConstValue::Indirect - // The alloc contains N × (ptr, len) pairs for each &str element. - // We can construct an array type with the known length and destructure it. - let len = match const_val { - mir::ConstValue::Slice { meta, .. } => meta, - _ => return Vec::new(), - }; - - // Build [&str; len] type and Indirect const for the slice data - let elem_ty = match inner_ty.kind() { - ty::Slice(elem) => *elem, - _ => return Vec::new(), - }; - let array_ty = Ty::new_array(tcx, elem_ty, len); - - let array_const = match const_val { - mir::ConstValue::Slice { alloc_id, .. } => { - mir::ConstValue::Indirect { alloc_id, offset: rustc_abi::Size::ZERO } - } - _ => return Vec::new(), - }; - - self.extract_str_array_from_const(tcx, array_const, array_ty) - } - - /// Extracts strings from a `ConstValue` representing `[&str; N]`. - fn extract_str_array_from_const<'b>( - &self, - tcx: TyCtxt<'tcx>, - const_val: mir::ConstValue, - array_ty: Ty<'tcx>, - ) -> Vec<&'b [u8]> - where - 'tcx: 'b, - { - let Some(destructured) = - tcx.try_destructure_mir_constant_for_user_output(const_val, array_ty) - else { - return Vec::new(); - }; - - destructured - .fields - .iter() - .filter_map(|(field_val, _field_ty)| field_val.try_get_slice_bytes_for_diagnostics(tcx)) - .collect() - } - fn codegen_argument( &mut self, bx: &mut Bx, diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index bc3ffa24d5289..fed8d65ea98ec 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -94,6 +94,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } + mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation( + ref symbols, + )) => { + let strings: Vec<&[u8]> = symbols.iter().map(|s| s.as_str().as_bytes()).collect(); + bx.debug_annotation(&strings); + } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d5f69d78ea6ae..53792f6694c19 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -776,6 +776,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let count = self.eval_operand(count, None)?; self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true) } + NonDivergingIntrinsic::DebugAnnotation(_) => interp_ok(()), } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index bf3d595e99436..0da8801e028c4 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -870,6 +870,16 @@ impl Display for NonDivergingIntrinsic<'_> { Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => { write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})") } + Self::DebugAnnotation(symbols) => { + write!(f, "debug_annotation(")?; + for (i, sym) in symbols.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{sym:?}")?; + } + write!(f, ")") + } } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 455089f285d17..23e14efe7c324 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -504,6 +504,15 @@ pub enum NonDivergingIntrinsic<'tcx> { /// **Needs clarification**: Is this typed or not, ie is there a typed load and store involved? /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), + + /// Carries user-specified debug annotation strings, extracted during MIR building. + /// Codegen emits these as backend-specific debug metadata (e.g., `@llvm.codeview.annotation` + /// on MSVC targets). No runtime effect — purely debug metadata. + DebugAnnotation( + #[type_foldable(identity)] + #[type_visitable(ignore)] + Box<[rustc_span::Symbol]>, + ), } /// Describes what kind of retag is to be performed. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 16a8743a6d67b..914f394b720c7 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -495,6 +495,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::DebugAnnotation(_) => {} } } StatementKind::BackwardIncompatibleDropHint { place, .. } => { diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 446b2939e3705..336fc642319e2 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -1,7 +1,7 @@ //! See docs in build/expr/mod.rs use rustc_abi::FieldIdx; -use rustc_ast::{AsmMacro, InlineAsmOptions}; +use rustc_ast::{AsmMacro, InlineAsmOptions, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; @@ -447,6 +447,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => rustc_middle::bug!(), } } + ExprKind::Call { ty, fun, ref args, .. } + if let ty::FnDef(def_id, _) = *ty.kind() + && let Some(intrinsic) = this.tcx.intrinsic(def_id) + && intrinsic.name == sym::debug_annotation => + { + let _fun = unpack!(block = this.as_local_operand(block, fun)); + + // Extract strings from THIR argument: &[&str; N] or &[&str] + let mut symbols = Vec::new(); + let mut extraction_failed = false; + if let Some(&arg_id) = args.first() { + Self::extract_debug_strings( + &this.thir, + arg_id, + &mut symbols, + &mut extraction_failed, + ); + } + + if extraction_failed || symbols.is_empty() { + this.tcx.dcx().span_err( + expr_span, + "debug_annotation requires constant string literal arguments", + ); + } else { + let source_info = this.source_info(expr_span); + this.cfg.push( + block, + Statement::new( + source_info, + StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::DebugAnnotation(symbols.into_boxed_slice()), + )), + ), + ); + } + + // Assign unit to destination + let unit_rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { + span: expr_span, + user_ty: None, + const_: Const::zero_sized(this.tcx.types.unit), + }))); + this.cfg.push_assign(block, this.source_info(expr_span), destination, unit_rvalue); + block.unit() + } ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => { let fun = unpack!(block = this.as_local_operand(block, fun)); let args: Box<[_]> = args @@ -899,4 +945,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => false, } } + + /// Recursively extract string literals from a THIR expression representing + /// `&[&str; N]`. Peels through `Scope` and `Borrow` wrappers to find the + /// inner `Array` of string `Literal`s. + fn extract_debug_strings( + thir: &Thir<'tcx>, + expr_id: ExprId, + symbols: &mut Vec, + failed: &mut bool, + ) { + match thir[expr_id].kind { + // Peel through scopes + ExprKind::Scope { value, .. } => { + Self::extract_debug_strings(thir, value, symbols, failed); + } + // Peel through borrows (&[...]) + ExprKind::Borrow { arg, .. } => { + Self::extract_debug_strings(thir, arg, symbols, failed); + } + // Peel through unsizing coercions (&[&str; N] → &[&str]) + ExprKind::PointerCoercion { source, .. } => { + Self::extract_debug_strings(thir, source, symbols, failed); + } + // Peel through derefs + ExprKind::Deref { arg } => { + Self::extract_debug_strings(thir, arg, symbols, failed); + } + // The array itself + ExprKind::Array { ref fields } => { + for &field_id in fields.iter() { + Self::extract_debug_strings(thir, field_id, symbols, failed); + } + } + // A string literal + ExprKind::Literal { lit, neg: false } if matches!(lit.node, LitKind::Str(..)) => { + if let LitKind::Str(sym, _) = lit.node { + symbols.push(sym); + } + } + // Anything else is unexpected + _ => { + *failed = true; + } + } + } } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 331c98fc198eb..c3b969641b4ae 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -87,6 +87,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += match **ndi { NonDivergingIntrinsic::Assume(..) => INSTR_COST, NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, + NonDivergingIntrinsic::DebugAnnotation(..) => 0, }; } StatementKind::Assign(..) => self.penalty += INSTR_COST, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 122429276e3c5..88c92bf0a047e 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -202,6 +202,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { }) => { // This statement represents `*dst = *src`, `count` times. } + NonDivergingIntrinsic::DebugAnnotation(_) => {} } } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index a1e5d810afc02..1e2f422f75c5b 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -399,6 +399,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) // copy_nonoverlapping takes pointers and mutated the pointed-to value. | StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(..)) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 656b74e15f727..454d7f5f77b82 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1540,6 +1540,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({op_cnt_ty} != usize)")) } } + StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(_)) => {} StatementKind::SetDiscriminant { place, .. } => { if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 51757c5827221..5eb42f443764d 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -465,6 +465,7 @@ pub struct CopyNonOverlapping { pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), + DebugAnnotation(Vec), } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index e1d9cf31036e2..074e97fe7da82 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -194,6 +194,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::DebugAnnotation(_) => {} }, StatementKind::ConstEvalCounter | StatementKind::Nop => {} } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index d25751c81f3f5..8535d97e04d16 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -510,6 +510,11 @@ impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> { count: copy_non_overlapping.count.stable(tables, cx), }) } + NonDivergingIntrinsic::DebugAnnotation(symbols) => { + crate::mir::NonDivergingIntrinsic::DebugAnnotation( + symbols.iter().map(|s| s.as_str().to_string()).collect(), + ) + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 766214fb67784..6bb797be0e5b4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -618,7 +618,6 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, - debug_annotation, coerce_pointee_validated, coerce_shared, coerce_unsized, @@ -750,6 +749,7 @@ symbols! { dead_code, dealloc, debug, + debug_annotation, debug_assert_eq_macro, debug_assert_macro, debug_assert_ne_macro, diff --git a/tests/ui/intrinsics/debug_annotation.rs b/tests/ui/intrinsics/debug_annotation.rs new file mode 100644 index 0000000000000..3a03093804953 --- /dev/null +++ b/tests/ui/intrinsics/debug_annotation.rs @@ -0,0 +1,41 @@ +//@ only-windows +// Verify debug_annotation behavior: happy paths and error cases. + +#![feature(debug_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::debug_annotation; + +// Single annotation via intrinsic +fn intrinsic_single() { + debug_annotation(&["test_annotation"]); +} + +// Multiple annotations via intrinsic +fn intrinsic_multiple() { + debug_annotation(&["category", "subcategory", "details"]); +} + +// Single annotation via macro +fn macro_single() { + std::hint::debug_annotation!("macro_test"); +} + +// Multiple annotations via macro +fn macro_multiple() { + std::hint::debug_annotation!("Performance", "HotPath", "Critical"); +} + +// Error case: non-constant argument +fn non_const_arg() { + let s = "hello"; + debug_annotation(&[s]); //~ ERROR debug_annotation requires constant string literal arguments +} + +fn main() { + intrinsic_single(); + intrinsic_multiple(); + macro_single(); + macro_multiple(); + non_const_arg(); +} diff --git a/tests/ui/intrinsics/debug_annotation.stderr b/tests/ui/intrinsics/debug_annotation.stderr new file mode 100644 index 0000000000000..7efdaad6a64d5 --- /dev/null +++ b/tests/ui/intrinsics/debug_annotation.stderr @@ -0,0 +1,8 @@ +error: debug_annotation requires constant string literal arguments + --> $DIR/debug_annotation.rs:32:5 + | +LL | debug_annotation(&[s]); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 8c801fe0e06773702d8edfbd375b91eb8cef1fbd Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Wed, 8 Apr 2026 18:18:33 +0530 Subject: [PATCH 08/36] Some cleanup --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 3 - compiler/rustc_codegen_llvm/src/intrinsic.rs | 9 -- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 2 - .../rustc_hir_analysis/src/check/intrinsic.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 7 +- .../rustc_mir_build/src/builder/expr/into.rs | 97 +++++++++---------- library/core/src/hint.rs | 16 +-- library/core/src/intrinsics/mod.rs | 17 ++-- 8 files changed, 68 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index dfe0abcff60aa..25dfdffcee1a6 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -389,9 +389,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::breakpoint => { unimplemented!(); } - sym::debug_annotation => { - bug!("debug_annotation should be handled in block.rs"); - } sym::va_arg => { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 275c79f578872..f69e8f1770e4f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -286,13 +286,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), - sym::debug_annotation => { - // Ideally should be codegenned here, but we get code genned - // operand (OperandRef) in this function instead of a MIR const - // that we can evaluate to get the string. Therefore as a hack - // we codegen directly in block.rs where we do have the const - bug!("debug_annotation should be handled in block.rs"); - } sym::va_arg => { match result.layout.backend_repr { BackendRepr::Scalar(scalar) => { @@ -842,8 +835,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } - /// HACK: this function does not belong in this impl at all - /// but it will do for now as we have to put it somewhere fn debug_annotation(&mut self, strings: &[&[u8]]) { if !self.cx.sess().target.is_like_msvc { return; diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index f68d063bb475f..274e9411036ce 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -51,7 +51,5 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { fn va_end(&mut self, val: Self::Value) -> Self::Value; /// Emit a debug annotation with the given strings. - /// The strings are passed as raw bytes that will become MDStrings in the LLVM IR. - /// On non-Windows targets or when debug info is not enabled, this is a no-op. fn debug_annotation(&mut self, strings: &[&[u8]]); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index df86eb7a50017..dfe17bde75688 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -295,7 +295,7 @@ pub(crate) fn check_intrinsic_type( sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::debug_annotation => { - // debug_annotation(annotations: &'static [&'static str]) -> () + // debug_annotation(strings: &'static [&'static str]) -> () // 0 type params, 0 const params, 1 input (&'static [&'static str]), returns unit let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); let slice_ty = Ty::new_slice(tcx, str_ref); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 23e14efe7c324..c0656a0e69a62 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -505,9 +505,10 @@ pub enum NonDivergingIntrinsic<'tcx> { /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), - /// Carries user-specified debug annotation strings, extracted during MIR building. - /// Codegen emits these as backend-specific debug metadata (e.g., `@llvm.codeview.annotation` - /// on MSVC targets). No runtime effect — purely debug metadata. + /// Denotes a debug annotation. + /// + /// Gets lowered to `llvm.codeview.annotation` on LLVM for MSVC targets. + /// Is a no-op on other targets and backends. DebugAnnotation( #[type_foldable(identity)] #[type_visitable(ignore)] diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 336fc642319e2..429790aa51dbe 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -366,8 +366,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None }) } - // Some intrinsics are handled here because they desperately want to avoid introducing - // unnecessary copies. + // Some intrinsics are handled here: + // - write_via_move and write_box_via_move because they desperately want + // to avoid introducing unnecessary copies. + // - debug_annoation because it is easier to extract its argument here + // than in codegen where it will require walking MIR. ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() && let Some(intrinsic) = this.tcx.intrinsic(def_id) @@ -444,54 +447,48 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } - _ => rustc_middle::bug!(), - } - } - ExprKind::Call { ty, fun, ref args, .. } - if let ty::FnDef(def_id, _) = *ty.kind() - && let Some(intrinsic) = this.tcx.intrinsic(def_id) - && intrinsic.name == sym::debug_annotation => - { - let _fun = unpack!(block = this.as_local_operand(block, fun)); + sym::debug_annotation => { + // Extract strings from argument + let mut symbols = Vec::new(); + let mut extraction_failed = false; + if let Some(&arg_id) = args.first() { + Self::extract_debug_strings( + &this.thir, + arg_id, + &mut symbols, + &mut extraction_failed, + ); + } - // Extract strings from THIR argument: &[&str; N] or &[&str] - let mut symbols = Vec::new(); - let mut extraction_failed = false; - if let Some(&arg_id) = args.first() { - Self::extract_debug_strings( - &this.thir, - arg_id, - &mut symbols, - &mut extraction_failed, - ); - } + if extraction_failed || symbols.is_empty() { + this.tcx.dcx().span_err( + expr_span, + "debug_annotation requires constant string literal arguments", + ); + } else { + let source_info = this.source_info(expr_span); + this.cfg.push( + block, + Statement::new( + source_info, + StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::DebugAnnotation(symbols.into_boxed_slice()), + )), + ), + ); + } - if extraction_failed || symbols.is_empty() { - this.tcx.dcx().span_err( - expr_span, - "debug_annotation requires constant string literal arguments", - ); - } else { - let source_info = this.source_info(expr_span); - this.cfg.push( - block, - Statement::new( - source_info, - StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::DebugAnnotation(symbols.into_boxed_slice()), - )), - ), - ); + // Assign unit to destination + let unit_rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { + span: expr_span, + user_ty: None, + const_: Const::zero_sized(this.tcx.types.unit), + }))); + this.cfg.push_assign(block, this.source_info(expr_span), destination, unit_rvalue); + block.unit() + } + _ => rustc_middle::bug!(), } - - // Assign unit to destination - let unit_rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { - span: expr_span, - user_ty: None, - const_: Const::zero_sized(this.tcx.types.unit), - }))); - this.cfg.push_assign(block, this.source_info(expr_span), destination, unit_rvalue); - block.unit() } ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => { let fun = unpack!(block = this.as_local_operand(block, fun)); @@ -946,8 +943,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Recursively extract string literals from a THIR expression representing - /// `&[&str; N]`. Peels through `Scope` and `Borrow` wrappers to find the + /// Recursively extract string literals from expr representing + /// `&[&str]`. Peels through `Scope` and `Borrow` wrappers to find the /// inner `Array` of string `Literal`s. fn extract_debug_strings( thir: &Thir<'tcx>, @@ -978,7 +975,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Self::extract_debug_strings(thir, field_id, symbols, failed); } } - // A string literal + // An individual string literal in the array ExprKind::Literal { lit, neg: false } if matches!(lit.node, LitKind::Str(..)) => { if let LitKind::Str(sym, _) = lit.node { symbols.push(sym); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6d030e0c2600e..34515c70dd9b9 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1025,16 +1025,18 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), } } -/// Emits a debug annotation with one or more labels that will appear in -/// PDB debug info on Windows. This is similar to MSVC's `__annotation()` -/// intrinsic. -/// -/// On non-Windows platforms, this macro is a no-op. + +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in `strings` being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with Windows targets and the LLVM backend. +/// Is a no-op on other targets and backends. #[unstable(feature = "debug_annotation", issue = "none")] #[macro_export] macro_rules! debug_annotation { - ($($msg:literal),+ $(,)?) => {{ - $crate::intrinsics::debug_annotation(&[$($msg),+]) + ($($string_:literal),+ $(,)?) => {{ + $crate::intrinsics::debug_annotation(&[$($string_),+]) }}; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3bee7c80011f6..39706896cd764 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -336,19 +336,16 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); -/// Emits a debug annotation that will appear in PDB debug info on Windows. -/// -/// It is equivalent to MSVC's `__annotation()` intrinsic. -/// -/// Takes a slice of string literal annotations that are lowered to an -/// MDTuple of MDStrings in the generated LLVM IR. All strings must be -/// compile-time constants. -/// -/// On non-Windows platforms this intrinsic is a no-op. +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in `strings` being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with Windows targets and the LLVM backend. +/// Is a no-op on other targets and backends. #[unstable(feature = "debug_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn debug_annotation(_annotations: &'static [&'static str]); +pub fn debug_annotation(_strings: &'static [&'static str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. From aa7d187da82636bdd74ac6f7143459c6d6b9a4f7 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 09:14:06 +0530 Subject: [PATCH 09/36] Added a missing match entry --- compiler/rustc_mir_build/src/builder/expr/into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 429790aa51dbe..6310ac1801d55 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -374,7 +374,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() && let Some(intrinsic) = this.tcx.intrinsic(def_id) - && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move) => + && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move | sym::debug_annotation) => { // We still have to evaluate the callee expression as normal (but we don't care // about its result). From 287451c85dd1db0e03606ee30a95fc3c36fc2764 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 11:18:53 +0530 Subject: [PATCH 10/36] Cleaned up and simplified --- compiler/rustc_middle/src/mir/syntax.rs | 2 +- .../rustc_mir_build/src/builder/expr/into.rs | 150 +++++++++--------- library/core/src/hint.rs | 2 +- library/core/src/intrinsics/mod.rs | 2 +- 4 files changed, 78 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index c0656a0e69a62..cb649dbcde601 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -506,7 +506,7 @@ pub enum NonDivergingIntrinsic<'tcx> { CopyNonOverlapping(CopyNonOverlapping<'tcx>), /// Denotes a debug annotation. - /// + /// /// Gets lowered to `llvm.codeview.annotation` on LLVM for MSVC targets. /// Is a no-op on other targets and backends. DebugAnnotation( diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 6310ac1801d55..053dcc62f421a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -370,11 +370,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // - write_via_move and write_box_via_move because they desperately want // to avoid introducing unnecessary copies. // - debug_annoation because it is easier to extract its argument here - // than in codegen where it will require walking MIR. + // than in codegen where it will require walking the MIR. ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() && let Some(intrinsic) = this.tcx.intrinsic(def_id) - && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move | sym::debug_annotation) => + && matches!( + intrinsic.name, + sym::write_via_move | sym::write_box_via_move | sym::debug_annotation + ) => { // We still have to evaluate the callee expression as normal (but we don't care // about its result). @@ -448,34 +451,71 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } sym::debug_annotation => { - // Extract strings from argument - let mut symbols = Vec::new(); - let mut extraction_failed = false; - if let Some(&arg_id) = args.first() { - Self::extract_debug_strings( - &this.thir, - arg_id, - &mut symbols, - &mut extraction_failed, - ); - } + // Extract string literals from `&["lit1", "lit2", ..]`. + // Peel wrapper nodes to find the Array, then read each + // element as a string literal. + let result: Option> = + args.first().and_then(|&arg_id| { + let thir = &this.thir; + let mut id = arg_id; + loop { + match thir[id].kind { + ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } => { + id = value; + } + ExprKind::Array { .. } => break, + _ => return None, + } + } + let ExprKind::Array { ref fields } = thir[id].kind else { + unreachable!() + }; + let mut syms = Vec::with_capacity(fields.len()); + for &field_id in fields.iter() { + let mut fid = field_id; + while let ExprKind::Scope { value, .. } = thir[fid].kind { + fid = value; + } + let ExprKind::Literal { lit, neg: false } = thir[fid].kind + else { + return None; + }; + let LitKind::Str(sym, _) = lit.node else { return None }; + syms.push(sym); + } + Some(syms) + }); - if extraction_failed || symbols.is_empty() { - this.tcx.dcx().span_err( - expr_span, - "debug_annotation requires constant string literal arguments", - ); - } else { - let source_info = this.source_info(expr_span); - this.cfg.push( - block, - Statement::new( - source_info, - StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::DebugAnnotation(symbols.into_boxed_slice()), - )), - ), - ); + match result { + Some(ref symbols) if !symbols.is_empty() => { + let source_info = this.source_info(expr_span); + this.cfg.push( + block, + Statement::new( + source_info, + StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::DebugAnnotation( + symbols.clone().into_boxed_slice(), + ), + )), + ), + ); + } + Some(_) => { + this.tcx.dcx().span_err( + expr_span, + "debug_annotation requires at least one string literal argument", + ); + } + None => { + this.tcx.dcx().span_err( + expr_span, + "debug_annotation requires constant string literal arguments", + ); + } } // Assign unit to destination @@ -484,7 +524,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: None, const_: Const::zero_sized(this.tcx.types.unit), }))); - this.cfg.push_assign(block, this.source_info(expr_span), destination, unit_rvalue); + this.cfg.push_assign( + block, + this.source_info(expr_span), + destination, + unit_rvalue, + ); block.unit() } _ => rustc_middle::bug!(), @@ -942,49 +987,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => false, } } - - /// Recursively extract string literals from expr representing - /// `&[&str]`. Peels through `Scope` and `Borrow` wrappers to find the - /// inner `Array` of string `Literal`s. - fn extract_debug_strings( - thir: &Thir<'tcx>, - expr_id: ExprId, - symbols: &mut Vec, - failed: &mut bool, - ) { - match thir[expr_id].kind { - // Peel through scopes - ExprKind::Scope { value, .. } => { - Self::extract_debug_strings(thir, value, symbols, failed); - } - // Peel through borrows (&[...]) - ExprKind::Borrow { arg, .. } => { - Self::extract_debug_strings(thir, arg, symbols, failed); - } - // Peel through unsizing coercions (&[&str; N] → &[&str]) - ExprKind::PointerCoercion { source, .. } => { - Self::extract_debug_strings(thir, source, symbols, failed); - } - // Peel through derefs - ExprKind::Deref { arg } => { - Self::extract_debug_strings(thir, arg, symbols, failed); - } - // The array itself - ExprKind::Array { ref fields } => { - for &field_id in fields.iter() { - Self::extract_debug_strings(thir, field_id, symbols, failed); - } - } - // An individual string literal in the array - ExprKind::Literal { lit, neg: false } if matches!(lit.node, LitKind::Str(..)) => { - if let LitKind::Str(sym, _) = lit.node { - symbols.push(sym); - } - } - // Anything else is unexpected - _ => { - *failed = true; - } - } - } } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 34515c70dd9b9..3ad7c09ca77ec 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1029,7 +1029,7 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { /// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) /// which results in `strings` being written to the /// PDB as an `S_ANNOTATION` record. -/// +/// /// Works only with Windows targets and the LLVM backend. /// Is a no-op on other targets and backends. #[unstable(feature = "debug_annotation", issue = "none")] diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 39706896cd764..2203e00a45904 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -339,7 +339,7 @@ pub fn breakpoint(); /// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) /// which results in `strings` being written to the /// PDB as an `S_ANNOTATION` record. -/// +/// /// Works only with Windows targets and the LLVM backend. /// Is a no-op on other targets and backends. #[unstable(feature = "debug_annotation", issue = "none")] From 671a6370ed124d37579be5e7969cfad45c182b84 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 11:27:29 +0530 Subject: [PATCH 11/36] Added more tests --- tests/ui/intrinsics/debug_annotation.rs | 19 +++++++++++++++++++ tests/ui/intrinsics/debug_annotation.stderr | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/ui/intrinsics/debug_annotation.rs b/tests/ui/intrinsics/debug_annotation.rs index 3a03093804953..349d42afd0829 100644 --- a/tests/ui/intrinsics/debug_annotation.rs +++ b/tests/ui/intrinsics/debug_annotation.rs @@ -32,10 +32,29 @@ fn non_const_arg() { debug_annotation(&[s]); //~ ERROR debug_annotation requires constant string literal arguments } +// Error case: function parameter +fn fn_param_arg(strs: &[&str]) { + debug_annotation(strs); //~ ERROR debug_annotation requires constant string literal arguments +} + +// Error case: named const +const MY_CONST: &str = "hello"; +fn named_const_arg() { + debug_annotation(&[MY_CONST]); //~ ERROR debug_annotation requires constant string literal arguments +} + +// Error case: empty array +fn empty_array() { + debug_annotation(&[]); //~ ERROR debug_annotation requires at least one string literal argument +} + fn main() { intrinsic_single(); intrinsic_multiple(); macro_single(); macro_multiple(); non_const_arg(); + fn_param_arg(&["a"]); + named_const_arg(); + empty_array(); } diff --git a/tests/ui/intrinsics/debug_annotation.stderr b/tests/ui/intrinsics/debug_annotation.stderr index 7efdaad6a64d5..8a9b814f49557 100644 --- a/tests/ui/intrinsics/debug_annotation.stderr +++ b/tests/ui/intrinsics/debug_annotation.stderr @@ -4,5 +4,23 @@ error: debug_annotation requires constant string literal arguments LL | debug_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: debug_annotation requires constant string literal arguments + --> $DIR/debug_annotation.rs:37:5 + | +LL | debug_annotation(strs); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: debug_annotation requires constant string literal arguments + --> $DIR/debug_annotation.rs:43:5 + | +LL | debug_annotation(&[MY_CONST]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: debug_annotation requires at least one string literal argument + --> $DIR/debug_annotation.rs:48:5 + | +LL | debug_annotation(&[]); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors From 04d8f70aaf88df36920b5a019ab3bf3f946fd22d Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 12:15:52 +0530 Subject: [PATCH 12/36] Renamed back to codeview_annotation --- compiler/rustc_borrowck/src/lib.rs | 2 +- .../src/polonius/legacy/loan_invalidations.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- .../rustc_codegen_ssa/src/mir/statement.rs | 4 +- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 4 +- .../src/interpret/intrinsics.rs | 2 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- compiler/rustc_middle/src/mir/syntax.rs | 4 +- compiler/rustc_middle/src/mir/visit.rs | 2 +- .../rustc_mir_build/src/builder/expr/into.rs | 12 ++-- .../rustc_mir_transform/src/cost_checker.rs | 2 +- .../src/dataflow_const_prop.rs | 2 +- .../rustc_mir_transform/src/jump_threading.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 2 +- compiler/rustc_public/src/mir/body.rs | 2 +- compiler/rustc_public/src/mir/visit.rs | 2 +- .../src/unstable/convert/stable/mir.rs | 4 +- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/hint.rs | 10 ++-- library/core/src/intrinsics/mod.rs | 4 +- ...g_annotation.rs => codeview_annotation.rs} | 12 ++-- ...ed.rs => codeview_annotation_optimized.rs} | 14 ++--- tests/ui/intrinsics/codeview_annotation.rs | 60 +++++++++++++++++++ .../ui/intrinsics/codeview_annotation.stderr | 26 ++++++++ tests/ui/intrinsics/debug_annotation.rs | 60 ------------------- tests/ui/intrinsics/debug_annotation.stderr | 26 -------- 29 files changed, 139 insertions(+), 139 deletions(-) rename tests/codegen-llvm/intrinsics/{debug_annotation.rs => codeview_annotation.rs} (78%) rename tests/codegen-llvm/intrinsics/{debug_annotation_optimized.rs => codeview_annotation_optimized.rs} (82%) create mode 100644 tests/ui/intrinsics/codeview_annotation.rs create mode 100644 tests/ui/intrinsics/codeview_annotation.stderr delete mode 100644 tests/ui/intrinsics/debug_annotation.rs delete mode 100644 tests/ui/intrinsics/debug_annotation.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d1ce5eb9f7ef0..d778c18a4765f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -829,7 +829,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, span, "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", ), - NonDivergingIntrinsic::DebugAnnotation(_) => {}, + NonDivergingIntrinsic::CodeviewAnnotation(_) => {}, } // Only relevant for mir typeck StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index eb9a47e6ef750..05c4bb88fe5ff 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, dst); self.consume_operand(location, count); } - StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(_)) => {} + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => {} // Only relevant for mir typeck StatementKind::AscribeUserType(..) // Only relevant for liveness and unsafeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d436210442416..7b22460271dc5 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -691,7 +691,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) - | StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(..)) | StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 25dfdffcee1a6..70d033ea3be18 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -717,7 +717,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn debug_annotation(&mut self, _strings: &[&[u8]]) { + fn codeview_annotation(&mut self, _strings: &[&[u8]]) { // No-op as unlike LLVM GGC has no intrinsic we can lower to } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index f69e8f1770e4f..4d9c847cbf38f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -835,7 +835,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } - fn debug_annotation(&mut self, strings: &[&[u8]]) { + fn codeview_annotation(&mut self, strings: &[&[u8]]) { if !self.cx.sess().target.is_like_msvc { return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index fed8d65ea98ec..0df9f9432969b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -94,11 +94,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } - mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation( + mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation( ref symbols, )) => { let strings: Vec<&[u8]> = symbols.iter().map(|s| s.as_str().as_bytes()).collect(); - bx.debug_annotation(&strings); + bx.codeview_annotation(&strings); } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 274e9411036ce..1cd93b67dadea 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -50,6 +50,6 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; - /// Emit a debug annotation with the given strings. - fn debug_annotation(&mut self, strings: &[&[u8]]); + /// Emit a codeview_annotation with the given strings. + fn codeview_annotation(&mut self, strings: &[&[u8]]); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 53792f6694c19..3cc64d74c5425 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -776,7 +776,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let count = self.eval_operand(count, None)?; self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true) } - NonDivergingIntrinsic::DebugAnnotation(_) => interp_ok(()), + NonDivergingIntrinsic::CodeviewAnnotation(_) => interp_ok(()), } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index dfe17bde75688..4d1535c5b61b9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -102,7 +102,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ctlz | sym::ctpop | sym::cttz - | sym::debug_annotation + | sym::codeview_annotation | sym::discriminant_value | sym::exp2f16 | sym::exp2f32 @@ -294,8 +294,8 @@ pub(crate) fn check_intrinsic_type( sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), - sym::debug_annotation => { - // debug_annotation(strings: &'static [&'static str]) -> () + sym::codeview_annotation => { + // codeview_annotation(strings: &'static [&'static str]) -> () // 0 type params, 0 const params, 1 input (&'static [&'static str]), returns unit let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); let slice_ty = Ty::new_slice(tcx, str_ref); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 0da8801e028c4..196d995fd5566 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -870,8 +870,8 @@ impl Display for NonDivergingIntrinsic<'_> { Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => { write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})") } - Self::DebugAnnotation(symbols) => { - write!(f, "debug_annotation(")?; + Self::CodeviewAnnotation(symbols) => { + write!(f, "codeview_annotation(")?; for (i, sym) in symbols.iter().enumerate() { if i > 0 { write!(f, ", ")?; diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index cb649dbcde601..b4bdc53ba7b93 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -505,11 +505,11 @@ pub enum NonDivergingIntrinsic<'tcx> { /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), - /// Denotes a debug annotation. + /// Denotes a codeview_annotation. /// /// Gets lowered to `llvm.codeview.annotation` on LLVM for MSVC targets. /// Is a no-op on other targets and backends. - DebugAnnotation( + CodeviewAnnotation( #[type_foldable(identity)] #[type_visitable(ignore)] Box<[rustc_span::Symbol]>, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 914f394b720c7..4198f4ef67006 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -495,7 +495,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } - NonDivergingIntrinsic::DebugAnnotation(_) => {} + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} } } StatementKind::BackwardIncompatibleDropHint { place, .. } => { diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 053dcc62f421a..d3e6180b95190 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -376,7 +376,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && let Some(intrinsic) = this.tcx.intrinsic(def_id) && matches!( intrinsic.name, - sym::write_via_move | sym::write_box_via_move | sym::debug_annotation + sym::write_via_move | sym::write_box_via_move | sym::codeview_annotation ) => { // We still have to evaluate the callee expression as normal (but we don't care @@ -450,7 +450,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } - sym::debug_annotation => { + sym::codeview_annotation => { // Extract string literals from `&["lit1", "lit2", ..]`. // Peel wrapper nodes to find the Array, then read each // element as a string literal. @@ -471,7 +471,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } let ExprKind::Array { ref fields } = thir[id].kind else { - unreachable!() + span_bug!(expr_span, "expected Array expr in codeview_annotation") }; let mut syms = Vec::with_capacity(fields.len()); for &field_id in fields.iter() { @@ -497,7 +497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Statement::new( source_info, StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::DebugAnnotation( + NonDivergingIntrinsic::CodeviewAnnotation( symbols.clone().into_boxed_slice(), ), )), @@ -507,13 +507,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(_) => { this.tcx.dcx().span_err( expr_span, - "debug_annotation requires at least one string literal argument", + "codeview_annotation requires at least one string literal argument", ); } None => { this.tcx.dcx().span_err( expr_span, - "debug_annotation requires constant string literal arguments", + "codeview_annotation requires constant string literal arguments", ); } } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index c3b969641b4ae..ebc99cd2d3d3a 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -87,7 +87,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += match **ndi { NonDivergingIntrinsic::Assume(..) => INSTR_COST, NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, - NonDivergingIntrinsic::DebugAnnotation(..) => 0, + NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, }; } StatementKind::Assign(..) => self.penalty += INSTR_COST, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 88c92bf0a047e..c9774da3a16e8 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -202,7 +202,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { }) => { // This statement represents `*dst = *src`, `count` times. } - NonDivergingIntrinsic::DebugAnnotation(_) => {} + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} } } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 1e2f422f75c5b..26c6b5422fc1f 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -399,7 +399,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) // copy_nonoverlapping takes pointers and mutated the pointed-to value. | StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) - | StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(..)) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 454d7f5f77b82..a2119e670ad76 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1540,7 +1540,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({op_cnt_ty} != usize)")) } } - StatementKind::Intrinsic(box NonDivergingIntrinsic::DebugAnnotation(_)) => {} + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => {} StatementKind::SetDiscriminant { place, .. } => { if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 5eb42f443764d..3cd35e9e78c1e 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -465,7 +465,7 @@ pub struct CopyNonOverlapping { pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), - DebugAnnotation(Vec), + CodeviewAnnotation(Vec), } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 074e97fe7da82..0ccd0e14e4e9a 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -194,7 +194,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } - NonDivergingIntrinsic::DebugAnnotation(_) => {} + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} }, StatementKind::ConstEvalCounter | StatementKind::Nop => {} } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 8535d97e04d16..f22145da33e6e 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -510,8 +510,8 @@ impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> { count: copy_non_overlapping.count.stable(tables, cx), }) } - NonDivergingIntrinsic::DebugAnnotation(symbols) => { - crate::mir::NonDivergingIntrinsic::DebugAnnotation( + NonDivergingIntrinsic::CodeviewAnnotation(symbols) => { + crate::mir::NonDivergingIntrinsic::CodeviewAnnotation( symbols.iter().map(|s| s.as_str().to_string()).collect(), ) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6bb797be0e5b4..bdfc08c334d64 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -749,7 +749,7 @@ symbols! { dead_code, dealloc, debug, - debug_annotation, + codeview_annotation, debug_assert_eq_macro, debug_assert_macro, debug_assert_ne_macro, diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 3ad7c09ca77ec..1076522af0d1d 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1032,14 +1032,14 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { /// /// Works only with Windows targets and the LLVM backend. /// Is a no-op on other targets and backends. -#[unstable(feature = "debug_annotation", issue = "none")] +#[unstable(feature = "codeview_annotation", issue = "none")] #[macro_export] -macro_rules! debug_annotation { +macro_rules! codeview_annotation { ($($string_:literal),+ $(,)?) => {{ - $crate::intrinsics::debug_annotation(&[$($string_),+]) + $crate::intrinsics::codeview_annotation(&[$($string_),+]) }}; } -#[unstable(feature = "debug_annotation", issue = "none")] +#[unstable(feature = "codeview_annotation", issue = "none")] #[doc(inline)] -pub use debug_annotation; +pub use codeview_annotation; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2203e00a45904..614dcfce7e71f 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -342,10 +342,10 @@ pub fn breakpoint(); /// /// Works only with Windows targets and the LLVM backend. /// Is a no-op on other targets and backends. -#[unstable(feature = "debug_annotation", issue = "none")] +#[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn debug_annotation(_strings: &'static [&'static str]); +pub fn codeview_annotation(_strings: &'static [&'static str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. diff --git a/tests/codegen-llvm/intrinsics/debug_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs similarity index 78% rename from tests/codegen-llvm/intrinsics/debug_annotation.rs rename to tests/codegen-llvm/intrinsics/codeview_annotation.rs index b49b7f06b4975..13cf423c87004 100644 --- a/tests/codegen-llvm/intrinsics/debug_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -2,37 +2,37 @@ //@ compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(debug_annotation)] +#![feature(codeview_annotation)] #![feature(core_intrinsics)] -use std::intrinsics::debug_annotation; +use std::intrinsics::codeview_annotation; // CHECK-LABEL: @intrinsic_single_annotation // CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) #[no_mangle] pub fn intrinsic_single_annotation() { - debug_annotation(&["test_annotation"]); + codeview_annotation(&["test_annotation"]); } // CHECK-LABEL: @intrinsic_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) #[no_mangle] pub fn intrinsic_multiple_annotations() { - debug_annotation(&["category", "subcategory", "details"]); + codeview_annotation(&["category", "subcategory", "details"]); } // CHECK-LABEL: @macro_single_annotation // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_SINGLE:![0-9]+]]) #[no_mangle] pub fn macro_single_annotation() { - std::hint::debug_annotation!("macro_test"); + std::hint::codeview_annotation!("macro_test"); } // CHECK-LABEL: @macro_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_MULTI:![0-9]+]]) #[no_mangle] pub fn macro_multiple_annotations() { - std::hint::debug_annotation!("Performance", "HotPath", "Critical"); + std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); } // Metadata definitions are at the end of LLVM IR, so check them here diff --git a/tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs similarity index 82% rename from tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs rename to tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs index d9d0583547e23..feb439f9dc393 100644 --- a/tests/codegen-llvm/intrinsics/debug_annotation_optimized.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs @@ -1,15 +1,15 @@ //@ only-windows //@ compile-flags: -Copt-level=2 // -// Verify that debug annotations survive LLVM optimizations. +// Verify that codeview_annotations survive LLVM optimizations. // The llvm.codeview.annotation intrinsic is marked noduplicate and // writes to inaccessible memory, so LLVM must not remove it. #![crate_type = "lib"] -#![feature(debug_annotation)] +#![feature(codeview_annotation)] #![feature(core_intrinsics)] -use std::intrinsics::debug_annotation; +use std::intrinsics::codeview_annotation; // Verify that an annotation inside a function with other code is not // removed even when surrounding code is optimized. @@ -17,7 +17,7 @@ use std::intrinsics::debug_annotation; // CHECK: call void @llvm.codeview.annotation(metadata [[COMP:![0-9]+]]) #[no_mangle] pub fn annotation_with_computation(x: u32) -> u32 { - debug_annotation(&["in_computation"]); + codeview_annotation(&["in_computation"]); x.wrapping_mul(31).wrapping_add(17) } @@ -27,8 +27,8 @@ pub fn annotation_with_computation(x: u32) -> u32 { // CHECK: call void @llvm.codeview.annotation(metadata [[SECOND:![0-9]+]]) #[no_mangle] pub fn multiple_annotations_same_fn() { - debug_annotation(&["first"]); - debug_annotation(&["second"]); + codeview_annotation(&["first"]); + codeview_annotation(&["second"]); } // Verify that annotations survive inlining: the annotation from @@ -42,7 +42,7 @@ pub fn annotation_after_inlining() { #[inline(always)] fn annotated_helper() { - debug_annotation(&["from_inlined_fn"]); + codeview_annotation(&["from_inlined_fn"]); } // CHECK-DAG: [[COMP]] = !{!"in_computation"} diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs new file mode 100644 index 0000000000000..5e2f3cb4e0241 --- /dev/null +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -0,0 +1,60 @@ +//@ only-windows +// Verify codeview_annotation behavior: happy paths and error cases. + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// Single annotation via intrinsic +fn intrinsic_single() { + codeview_annotation(&["test_annotation"]); +} + +// Multiple annotations via intrinsic +fn intrinsic_multiple() { + codeview_annotation(&["category", "subcategory", "details"]); +} + +// Single annotation via macro +fn macro_single() { + std::hint::codeview_annotation!("macro_test"); +} + +// Multiple annotations via macro +fn macro_multiple() { + std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); +} + +// Error case: non-constant argument +fn non_const_arg() { + let s = "hello"; + codeview_annotation(&[s]); //~ ERROR codeview_annotation requires constant string literal arguments +} + +// Error case: function parameter +fn fn_param_arg(strs: &[&str]) { + codeview_annotation(strs); //~ ERROR codeview_annotation requires constant string literal arguments +} + +// Error case: named const +const MY_CONST: &str = "hello"; +fn named_const_arg() { + codeview_annotation(&[MY_CONST]); //~ ERROR codeview_annotation requires constant string literal arguments +} + +// Error case: empty array +fn empty_array() { + codeview_annotation(&[]); //~ ERROR codeview_annotation requires at least one string literal argument +} + +fn main() { + intrinsic_single(); + intrinsic_multiple(); + macro_single(); + macro_multiple(); + non_const_arg(); + fn_param_arg(&["a"]); + named_const_arg(); + empty_array(); +} diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr new file mode 100644 index 0000000000000..43c6f4fc3f34c --- /dev/null +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -0,0 +1,26 @@ +error: codeview_annotation requires constant string literal arguments + --> $DIR/codeview_annotation.rs:32:5 + | +LL | codeview_annotation(&[s]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: codeview_annotation requires constant string literal arguments + --> $DIR/codeview_annotation.rs:37:5 + | +LL | codeview_annotation(strs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: codeview_annotation requires constant string literal arguments + --> $DIR/codeview_annotation.rs:43:5 + | +LL | codeview_annotation(&[MY_CONST]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: codeview_annotation requires at least one string literal argument + --> $DIR/codeview_annotation.rs:48:5 + | +LL | codeview_annotation(&[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/intrinsics/debug_annotation.rs b/tests/ui/intrinsics/debug_annotation.rs deleted file mode 100644 index 349d42afd0829..0000000000000 --- a/tests/ui/intrinsics/debug_annotation.rs +++ /dev/null @@ -1,60 +0,0 @@ -//@ only-windows -// Verify debug_annotation behavior: happy paths and error cases. - -#![feature(debug_annotation)] -#![feature(core_intrinsics)] - -use std::intrinsics::debug_annotation; - -// Single annotation via intrinsic -fn intrinsic_single() { - debug_annotation(&["test_annotation"]); -} - -// Multiple annotations via intrinsic -fn intrinsic_multiple() { - debug_annotation(&["category", "subcategory", "details"]); -} - -// Single annotation via macro -fn macro_single() { - std::hint::debug_annotation!("macro_test"); -} - -// Multiple annotations via macro -fn macro_multiple() { - std::hint::debug_annotation!("Performance", "HotPath", "Critical"); -} - -// Error case: non-constant argument -fn non_const_arg() { - let s = "hello"; - debug_annotation(&[s]); //~ ERROR debug_annotation requires constant string literal arguments -} - -// Error case: function parameter -fn fn_param_arg(strs: &[&str]) { - debug_annotation(strs); //~ ERROR debug_annotation requires constant string literal arguments -} - -// Error case: named const -const MY_CONST: &str = "hello"; -fn named_const_arg() { - debug_annotation(&[MY_CONST]); //~ ERROR debug_annotation requires constant string literal arguments -} - -// Error case: empty array -fn empty_array() { - debug_annotation(&[]); //~ ERROR debug_annotation requires at least one string literal argument -} - -fn main() { - intrinsic_single(); - intrinsic_multiple(); - macro_single(); - macro_multiple(); - non_const_arg(); - fn_param_arg(&["a"]); - named_const_arg(); - empty_array(); -} diff --git a/tests/ui/intrinsics/debug_annotation.stderr b/tests/ui/intrinsics/debug_annotation.stderr deleted file mode 100644 index 8a9b814f49557..0000000000000 --- a/tests/ui/intrinsics/debug_annotation.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: debug_annotation requires constant string literal arguments - --> $DIR/debug_annotation.rs:32:5 - | -LL | debug_annotation(&[s]); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: debug_annotation requires constant string literal arguments - --> $DIR/debug_annotation.rs:37:5 - | -LL | debug_annotation(strs); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: debug_annotation requires constant string literal arguments - --> $DIR/debug_annotation.rs:43:5 - | -LL | debug_annotation(&[MY_CONST]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: debug_annotation requires at least one string literal argument - --> $DIR/debug_annotation.rs:48:5 - | -LL | debug_annotation(&[]); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - From 2650d06a3db21a97969d387c170e06e7d55ee201 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 13:18:56 +0530 Subject: [PATCH 13/36] Replaced loop with while loop --- .../rustc_mir_build/src/builder/expr/into.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index d3e6180b95190..b8092baf31b9d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -458,20 +458,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { args.first().and_then(|&arg_id| { let thir = &this.thir; let mut id = arg_id; - loop { - match thir[id].kind { - ExprKind::Scope { value, .. } - | ExprKind::Borrow { arg: value, .. } - | ExprKind::PointerCoercion { source: value, .. } - | ExprKind::Deref { arg: value } => { - id = value; - } - ExprKind::Array { .. } => break, - _ => return None, - } + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } = thir[id].kind + { + id = value; } let ExprKind::Array { ref fields } = thir[id].kind else { - span_bug!(expr_span, "expected Array expr in codeview_annotation") + return None; }; let mut syms = Vec::with_capacity(fields.len()); for &field_id in fields.iter() { From 01c0530ab09f622b2cf478d5a505393b4001d93b Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 13:50:01 +0530 Subject: [PATCH 14/36] Added support for const args --- .../rustc_mir_build/src/builder/expr/into.rs | 156 ++++++++++++++---- tests/ui/intrinsics/codeview_annotation.rs | 40 ++++- .../ui/intrinsics/codeview_annotation.stderr | 14 +- 3 files changed, 157 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index b8092baf31b9d..a487cba48fe2e 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -6,11 +6,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; +use rustc_middle::mir::interpret::GlobalId; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::thir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::{DUMMY_SP, Spanned, sym}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt}; +use rustc_span::{DUMMY_SP, Spanned, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; @@ -369,7 +370,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Some intrinsics are handled here: // - write_via_move and write_box_via_move because they desperately want // to avoid introducing unnecessary copies. - // - debug_annoation because it is easier to extract its argument here + // - codeview_annoation because it is easier to extract its argument here // than in codegen where it will require walking the MIR. ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() @@ -451,38 +452,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } sym::codeview_annotation => { - // Extract string literals from `&["lit1", "lit2", ..]`. - // Peel wrapper nodes to find the Array, then read each - // element as a string literal. - let result: Option> = - args.first().and_then(|&arg_id| { - let thir = &this.thir; - let mut id = arg_id; - while let ExprKind::Scope { value, .. } - | ExprKind::Borrow { arg: value, .. } - | ExprKind::PointerCoercion { source: value, .. } - | ExprKind::Deref { arg: value } = thir[id].kind - { - id = value; - } - let ExprKind::Array { ref fields } = thir[id].kind else { - return None; - }; - let mut syms = Vec::with_capacity(fields.len()); - for &field_id in fields.iter() { - let mut fid = field_id; - while let ExprKind::Scope { value, .. } = thir[fid].kind { - fid = value; - } - let ExprKind::Literal { lit, neg: false } = thir[fid].kind - else { - return None; - }; - let LitKind::Str(sym, _) = lit.node else { return None }; - syms.push(sym); - } - Some(syms) - }); + let result = args.first().and_then(|&arg_id| { + extract_codeview_annotation_strings(this.tcx, &this.thir, arg_id) + }); match result { Some(ref symbols) if !symbols.is_empty() => { @@ -983,3 +955,117 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } + +/// Extract string symbols from a `codeview_annotation` argument expression. +/// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) +/// to find either an inline Array of string literals/named consts, or a +/// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. +fn extract_codeview_annotation_strings<'tcx>( + tcx: TyCtxt<'tcx>, + thir: &Thir<'tcx>, + arg_id: ExprId, +) -> Option> { + let mut id = arg_id; + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } = thir[id].kind + { + id = value; + } + match thir[id].kind { + // Inline array: &["lit1", "lit2"] or &[CONST1, "lit2"] + ExprKind::Array { ref fields } => { + let mut syms = Vec::with_capacity(fields.len()); + for &field_id in fields.iter() { + let mut fid = field_id; + while let ExprKind::Scope { value, .. } = thir[fid].kind { + fid = value; + } + match thir[fid].kind { + ExprKind::Literal { lit, neg: false } + if let LitKind::Str(sym, _) = lit.node => + { + syms.push(sym); + } + ExprKind::NamedConst { def_id, args, .. } => { + let s = eval_named_const_as_str(tcx, def_id, args, thir[fid].ty)?; + syms.push(s); + } + _ => return None, + } + } + Some(syms) + } + // Whole named const: STRS where STRS: &[&str] or [&str; N] + ExprKind::NamedConst { def_id, args, .. } => { + eval_named_const_as_str_slice(tcx, def_id, args, thir[id].ty) + } + _ => None, + } +} + +/// Evaluate a `NamedConst` with type `&str` to a `Symbol`. +fn eval_named_const_as_str<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: rustc_hir::def_id::DefId, + args: ty::GenericArgsRef<'tcx>, + ty: Ty<'tcx>, +) -> Option { + let instance = ty::Instance::new_raw(def_id, args); + let cid = GlobalId { instance, promoted: None }; + let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); + let Ok(Ok(valtree)) = tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP) else { + return None; + }; + let value = ty::Value { ty, valtree }; + let bytes = value.try_to_raw_bytes(tcx)?; + let s = std::str::from_utf8(bytes).ok()?; + Some(Symbol::intern(s)) +} + +/// Evaluate a `NamedConst` with type `&[&str]` or `[&str; N]` to a `Vec`. +/// Walks the ValTree structure: peels references, iterates array/slice branches, +/// and reads each `&str` element's raw bytes. +fn eval_named_const_as_str_slice<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: rustc_hir::def_id::DefId, + args: ty::GenericArgsRef<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + let instance = ty::Instance::new_raw(def_id, args); + let cid = GlobalId { instance, promoted: None }; + let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); + let Ok(Ok(valtree)) = tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP) else { + return None; + }; + let value = ty::Value { ty, valtree }; + extract_strs_from_value(tcx, value) +} + +/// Recursively extract string symbols from a `ty::Value`. +/// Handles `&str`, `&[&str]`, `[&str; N]`, and `&[&str; N]`. +fn extract_strs_from_value<'tcx>(tcx: TyCtxt<'tcx>, value: ty::Value<'tcx>) -> Option> { + match value.ty.kind() { + ty::Ref(_, inner_ty, _) if inner_ty.is_str() => { + let bytes = value.try_to_raw_bytes(tcx)?; + let s = std::str::from_utf8(bytes).ok()?; + Some(vec![Symbol::intern(s)]) + } + ty::Ref(_, inner_ty, _) => { + // Peel reference: &[&str] or &[&str; N] + extract_strs_from_value(tcx, ty::Value { ty: *inner_ty, valtree: value.valtree }) + } + ty::Array(..) | ty::Slice(_) => { + let elems = value.try_to_branch()?; + let mut syms = Vec::with_capacity(elems.len()); + for c in elems { + let elem_value = c.try_to_value()?; + let mut elem_syms = extract_strs_from_value(tcx, elem_value)?; + syms.append(&mut elem_syms); + } + Some(syms) + } + _ => None, + } +} diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 5e2f3cb4e0241..8c750cca1c15e 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -26,7 +26,34 @@ fn macro_multiple() { std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); } -// Error case: non-constant argument +// Named const elements +const ANNOTATION_A: &str = "hello"; +const ANNOTATION_B: &str = "there"; + +fn named_const_elements() { + codeview_annotation(&[ANNOTATION_A, ANNOTATION_B]); +} + +// Named const and literal +fn mixed_const_and_literal() { + codeview_annotation(&[ANNOTATION_A, "world"]); +} + +// Const slice +const STRS_SLICE: &[&str] = &["hello", "there"]; + +fn named_const_slice() { + codeview_annotation(STRS_SLICE); +} + +// Ref to named const array +const STRS_ARRAY: [&str; 2] = ["hello", "there"]; + +fn named_const_array_ref() { + codeview_annotation(&STRS_ARRAY); +} + +// Error case: local variable fn non_const_arg() { let s = "hello"; codeview_annotation(&[s]); //~ ERROR codeview_annotation requires constant string literal arguments @@ -37,12 +64,6 @@ fn fn_param_arg(strs: &[&str]) { codeview_annotation(strs); //~ ERROR codeview_annotation requires constant string literal arguments } -// Error case: named const -const MY_CONST: &str = "hello"; -fn named_const_arg() { - codeview_annotation(&[MY_CONST]); //~ ERROR codeview_annotation requires constant string literal arguments -} - // Error case: empty array fn empty_array() { codeview_annotation(&[]); //~ ERROR codeview_annotation requires at least one string literal argument @@ -53,8 +74,11 @@ fn main() { intrinsic_multiple(); macro_single(); macro_multiple(); + named_const_elements(); + mixed_const_and_literal(); + named_const_slice(); + named_const_array_ref(); non_const_arg(); fn_param_arg(&["a"]); - named_const_arg(); empty_array(); } diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index 43c6f4fc3f34c..5e1fc73e82c40 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -1,26 +1,20 @@ error: codeview_annotation requires constant string literal arguments - --> $DIR/codeview_annotation.rs:32:5 + --> $DIR/codeview_annotation.rs:57:5 | LL | codeview_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: codeview_annotation requires constant string literal arguments - --> $DIR/codeview_annotation.rs:37:5 + --> $DIR/codeview_annotation.rs:62:5 | LL | codeview_annotation(strs); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires constant string literal arguments - --> $DIR/codeview_annotation.rs:43:5 - | -LL | codeview_annotation(&[MY_CONST]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: codeview_annotation requires at least one string literal argument - --> $DIR/codeview_annotation.rs:48:5 + --> $DIR/codeview_annotation.rs:67:5 | LL | codeview_annotation(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 06dcc883e13eca76770a5401c0daf2e40890bd33 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 14:24:44 +0530 Subject: [PATCH 15/36] Improved error messages --- compiler/rustc_mir_build/src/builder/expr/into.rs | 4 ++-- tests/ui/intrinsics/codeview_annotation.rs | 6 +++--- tests/ui/intrinsics/codeview_annotation.stderr | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index a487cba48fe2e..d7991d411c240 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -474,13 +474,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(_) => { this.tcx.dcx().span_err( expr_span, - "codeview_annotation requires at least one string literal argument", + "codeview_annotation requires a non-empty array argument", ); } None => { this.tcx.dcx().span_err( expr_span, - "codeview_annotation requires constant string literal arguments", + "codeview_annotation requires a constant argument", ); } } diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 8c750cca1c15e..993ae68a150c7 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -56,17 +56,17 @@ fn named_const_array_ref() { // Error case: local variable fn non_const_arg() { let s = "hello"; - codeview_annotation(&[s]); //~ ERROR codeview_annotation requires constant string literal arguments + codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a constant argument } // Error case: function parameter fn fn_param_arg(strs: &[&str]) { - codeview_annotation(strs); //~ ERROR codeview_annotation requires constant string literal arguments + codeview_annotation(strs); //~ ERROR codeview_annotation requires a constant argument } // Error case: empty array fn empty_array() { - codeview_annotation(&[]); //~ ERROR codeview_annotation requires at least one string literal argument + codeview_annotation(&[]); //~ ERROR codeview_annotation requires a non-empty array argument } fn main() { diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index 5e1fc73e82c40..cb185787b2847 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -1,17 +1,17 @@ -error: codeview_annotation requires constant string literal arguments - --> $DIR/codeview_annotation.rs:57:5 +error: codeview_annotation requires a constant argument + --> $DIR/codeview_annotation.rs:59:5 | LL | codeview_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires constant string literal arguments - --> $DIR/codeview_annotation.rs:62:5 +error: codeview_annotation requires a constant argument + --> $DIR/codeview_annotation.rs:64:5 | LL | codeview_annotation(strs); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires at least one string literal argument - --> $DIR/codeview_annotation.rs:67:5 +error: codeview_annotation requires a non-empty array argument + --> $DIR/codeview_annotation.rs:69:5 | LL | codeview_annotation(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^ From ff5a3353ecaee4f37672655daaa521408bf32272 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 16:07:39 +0530 Subject: [PATCH 16/36] Added support for consts --- .../rustc_mir_build/src/builder/expr/into.rs | 194 ++++++++---------- 1 file changed, 85 insertions(+), 109 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index d7991d411c240..ef2b6f57adf21 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -452,20 +452,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } sym::codeview_annotation => { - let result = args.first().and_then(|&arg_id| { - extract_codeview_annotation_strings(this.tcx, &this.thir, arg_id) - }); + let result = args + .first() + .and_then(|&arg_id| this.get_codeview_annotation_arg(arg_id)); match result { - Some(ref symbols) if !symbols.is_empty() => { - let source_info = this.source_info(expr_span); + Some(strings) if !strings.is_empty() => { this.cfg.push( block, Statement::new( - source_info, + this.source_info(expr_span), StatementKind::Intrinsic(Box::new( NonDivergingIntrinsic::CodeviewAnnotation( - symbols.clone().into_boxed_slice(), + strings.into_boxed_slice(), ), )), ), @@ -954,118 +953,95 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => false, } } -} -/// Extract string symbols from a `codeview_annotation` argument expression. -/// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) -/// to find either an inline Array of string literals/named consts, or a -/// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. -fn extract_codeview_annotation_strings<'tcx>( - tcx: TyCtxt<'tcx>, - thir: &Thir<'tcx>, - arg_id: ExprId, -) -> Option> { - let mut id = arg_id; - while let ExprKind::Scope { value, .. } - | ExprKind::Borrow { arg: value, .. } - | ExprKind::PointerCoercion { source: value, .. } - | ExprKind::Deref { arg: value } = thir[id].kind - { - id = value; - } - match thir[id].kind { - // Inline array: &["lit1", "lit2"] or &[CONST1, "lit2"] - ExprKind::Array { ref fields } => { - let mut syms = Vec::with_capacity(fields.len()); - for &field_id in fields.iter() { - let mut fid = field_id; - while let ExprKind::Scope { value, .. } = thir[fid].kind { - fid = value; - } - match thir[fid].kind { - ExprKind::Literal { lit, neg: false } - if let LitKind::Str(sym, _) = lit.node => - { - syms.push(sym); + /// Extract string symbols from a `codeview_annotation` argument expression. + /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) + /// to find either an inline Array of string literals/named consts, or a + /// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. + fn get_codeview_annotation_arg(&self, arg_id: ExprId) -> Option> { + // Peel away scopes, borrows etc. + let mut id = arg_id; + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } = self.thir[id].kind + { + id = value; + } + match self.thir[id].kind { + // Inline array: &["lit1", "lit2"] or &[CONST1, "lit2"] + ExprKind::Array { ref fields } => { + let mut syms = Vec::with_capacity(fields.len()); + for &field_id in fields.iter() { + let mut id = field_id; + while let ExprKind::Scope { value, .. } = self.thir[id].kind { + id = value; } - ExprKind::NamedConst { def_id, args, .. } => { - let s = eval_named_const_as_str(tcx, def_id, args, thir[fid].ty)?; - syms.push(s); + match self.thir[id].kind { + ExprKind::Literal { lit, neg: false } + if let LitKind::Str(sym, _) = lit.node => + { + syms.push(sym); + } + ExprKind::NamedConst { .. } => { + let mut strs = self.eval_named_const_strs(&self.thir[id])?; + if strs.len() != 1 { + // A array field has to be a single string + return None; + } + syms.append(&mut strs); + } + _ => return None, } - _ => return None, } + Some(syms) } - Some(syms) - } - // Whole named const: STRS where STRS: &[&str] or [&str; N] - ExprKind::NamedConst { def_id, args, .. } => { - eval_named_const_as_str_slice(tcx, def_id, args, thir[id].ty) + // Array that's a named const: e.g. const STRS: &[&str] = &["lit1", "lit2"]; + ExprKind::NamedConst { .. } => self.eval_named_const_strs(&self.thir[id]), + _ => None, } - _ => None, } -} -/// Evaluate a `NamedConst` with type `&str` to a `Symbol`. -fn eval_named_const_as_str<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: rustc_hir::def_id::DefId, - args: ty::GenericArgsRef<'tcx>, - ty: Ty<'tcx>, -) -> Option { - let instance = ty::Instance::new_raw(def_id, args); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - let Ok(Ok(valtree)) = tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP) else { - return None; - }; - let value = ty::Value { ty, valtree }; - let bytes = value.try_to_raw_bytes(tcx)?; - let s = std::str::from_utf8(bytes).ok()?; - Some(Symbol::intern(s)) -} - -/// Evaluate a `NamedConst` with type `&[&str]` or `[&str; N]` to a `Vec`. -/// Walks the ValTree structure: peels references, iterates array/slice branches, -/// and reads each `&str` element's raw bytes. -fn eval_named_const_as_str_slice<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: rustc_hir::def_id::DefId, - args: ty::GenericArgsRef<'tcx>, - ty: Ty<'tcx>, -) -> Option> { - let instance = ty::Instance::new_raw(def_id, args); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - let Ok(Ok(valtree)) = tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP) else { - return None; - }; - let value = ty::Value { ty, valtree }; - extract_strs_from_value(tcx, value) -} + /// Evaluate a `NamedConst` THIR expression to a list of string symbols. + /// Works for `&str` (returns one symbol), `&[&str]`, `[&str; N]`, etc. + fn eval_named_const_strs(&self, expr: &Expr<'tcx>) -> Option> { + let ExprKind::NamedConst { def_id, args, .. } = expr.kind else { + return None; + }; + let instance = ty::Instance::new_raw(def_id, args); + let cid = GlobalId { instance, promoted: None }; + let typing_env = ty::TypingEnv::post_analysis(self.tcx, def_id); + let Ok(Ok(valtree)) = self.tcx.const_eval_global_id_for_typeck(typing_env, cid, expr.span) + else { + return None; + }; + Self::extract_strs_from_value(self.tcx, ty::Value { ty: expr.ty, valtree }) + } -/// Recursively extract string symbols from a `ty::Value`. -/// Handles `&str`, `&[&str]`, `[&str; N]`, and `&[&str; N]`. -fn extract_strs_from_value<'tcx>(tcx: TyCtxt<'tcx>, value: ty::Value<'tcx>) -> Option> { - match value.ty.kind() { - ty::Ref(_, inner_ty, _) if inner_ty.is_str() => { - let bytes = value.try_to_raw_bytes(tcx)?; - let s = std::str::from_utf8(bytes).ok()?; - Some(vec![Symbol::intern(s)]) - } - ty::Ref(_, inner_ty, _) => { - // Peel reference: &[&str] or &[&str; N] - extract_strs_from_value(tcx, ty::Value { ty: *inner_ty, valtree: value.valtree }) - } - ty::Array(..) | ty::Slice(_) => { - let elems = value.try_to_branch()?; - let mut syms = Vec::with_capacity(elems.len()); - for c in elems { - let elem_value = c.try_to_value()?; - let mut elem_syms = extract_strs_from_value(tcx, elem_value)?; - syms.append(&mut elem_syms); + /// Recursively extract string symbols from a `ty::Value`. + /// Handles `&str`, `&[&str]`, `[&str; N]`, and `&[&str; N]`. + fn extract_strs_from_value(tcx: TyCtxt<'tcx>, value: ty::Value<'tcx>) -> Option> { + match value.ty.kind() { + ty::Ref(_, inner_ty, _) if inner_ty.is_str() => { + let bytes = value.try_to_raw_bytes(tcx)?; + let s = std::str::from_utf8(bytes).ok()?; + Some(vec![Symbol::intern(s)]) + } + ty::Ref(_, inner_ty, _) => Self::extract_strs_from_value( + tcx, + ty::Value { ty: *inner_ty, valtree: value.valtree }, + ), + ty::Array(..) | ty::Slice(_) => { + let elems = value.try_to_branch()?; + let mut syms = Vec::with_capacity(elems.len()); + for c in elems { + let elem_value = c.try_to_value()?; + let mut elem_syms = Self::extract_strs_from_value(tcx, elem_value)?; + syms.append(&mut elem_syms); + } + Some(syms) } - Some(syms) + _ => None, } - _ => None, } } From 5b1dd58b7f9ff607bbafa3c6a655996550355b31 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 18:16:01 +0530 Subject: [PATCH 17/36] Lifetimes are no longer static --- compiler/rustc_codegen_cranelift/src/base.rs | 2 ++ .../rustc_codegen_cranelift/src/constant.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 14 ++++++++---- .../rustc_mir_build/src/builder/expr/into.rs | 22 +++++++++++-------- library/core/src/intrinsics/mod.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 ++ 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 4f483cdc5d6c7..d80dd12c60f4a 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -918,6 +918,8 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: StatementKind::Intrinsic(intrinsic) => match &**intrinsic { // We ignore `assume` intrinsics, they are only useful for optimizations NonDivergingIntrinsic::Assume(_) => {} + // CodeView annotations are only supported by the LLVM backend + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { src, dst, diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index ff8e6744bd32c..40facfc52d207 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -606,6 +606,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( StatementKind::Intrinsic(intrinsic) => match **intrinsic { NonDivergingIntrinsic::CopyNonOverlapping(..) => return None, NonDivergingIntrinsic::Assume(..) => {} + NonDivergingIntrinsic::CodeviewAnnotation(..) => {} }, // conservative handling StatementKind::Assign(_) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 4d1535c5b61b9..4ba978f36b703 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -295,11 +295,17 @@ pub(crate) fn check_intrinsic_type( sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::codeview_annotation => { - // codeview_annotation(strings: &'static [&'static str]) -> () - // 0 type params, 0 const params, 1 input (&'static [&'static str]), returns unit - let str_ref = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); + // codeview_annotation(strings: &[&str]) -> () + // Two bound lifetime regions: one for the outer &, one for the inner &str. + let br_outer = + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }; + let br_inner = + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon }; + let region_outer = ty::Region::new_bound(tcx, ty::INNERMOST, br_outer); + let region_inner = ty::Region::new_bound(tcx, ty::INNERMOST, br_inner); + let str_ref = Ty::new_imm_ref(tcx, region_inner, tcx.types.str_); let slice_ty = Ty::new_slice(tcx, str_ref); - let ref_to_slice = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, slice_ty); + let ref_to_slice = Ty::new_imm_ref(tcx, region_outer, slice_ty); (0, 0, vec![ref_to_slice], tcx.types.unit) } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index ef2b6f57adf21..fbd23c4276e31 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -452,11 +452,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } sym::codeview_annotation => { - let result = args + // Extract strings from the `&[&str]` argument of codeview_annoation + // and lower them into a CodviewAnnoation MIR intrinsic + let strings = args .first() - .and_then(|&arg_id| this.get_codeview_annotation_arg(arg_id)); + .and_then(|&arg_id| this.extract_strings_from_array(arg_id)); - match result { + match strings { Some(strings) if !strings.is_empty() => { this.cfg.push( block, @@ -954,13 +956,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Extract string symbols from a `codeview_annotation` argument expression. + /// Extract string from an string array expr. /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) - /// to find either an inline Array of string literals/named consts, or a + /// to find either an inline array of string literals/named consts, or a /// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. - fn get_codeview_annotation_arg(&self, arg_id: ExprId) -> Option> { + fn extract_strings_from_array(&self, expr_id: ExprId) -> Option> { // Peel away scopes, borrows etc. - let mut id = arg_id; + let mut id = expr_id; while let ExprKind::Scope { value, .. } | ExprKind::Borrow { arg: value, .. } | ExprKind::PointerCoercion { source: value, .. } @@ -969,12 +971,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { id = value; } match self.thir[id].kind { - // Inline array: &["lit1", "lit2"] or &[CONST1, "lit2"] ExprKind::Array { ref fields } => { let mut syms = Vec::with_capacity(fields.len()); for &field_id in fields.iter() { let mut id = field_id; - while let ExprKind::Scope { value, .. } = self.thir[id].kind { + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::Deref { arg: value } = self.thir[id].kind + { id = value; } match self.thir[id].kind { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 614dcfce7e71f..777e595d5800a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -345,7 +345,7 @@ pub fn breakpoint(); #[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_strings: &'static [&'static str]); +pub fn codeview_annotation(_strings: &[&str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1484b8c8bcc45..050a6478ae78f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -231,6 +231,8 @@ fn check_statement<'tcx>( StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(cx, op, span, body, msrv), + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => Ok(()), + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { dst, src, count }, )) => { From 32628fe7b19f32d376f0ad80d9a740397f0f850e Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 18:56:13 +0530 Subject: [PATCH 18/36] Simplified typeck code --- .../rustc_hir_analysis/src/check/intrinsic.rs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 4ba978f36b703..ce75bd5f474d4 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -295,17 +295,28 @@ pub(crate) fn check_intrinsic_type( sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::codeview_annotation => { - // codeview_annotation(strings: &[&str]) -> () - // Two bound lifetime regions: one for the outer &, one for the inner &str. - let br_outer = - ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }; - let br_inner = - ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon }; - let region_outer = ty::Region::new_bound(tcx, ty::INNERMOST, br_outer); - let region_inner = ty::Region::new_bound(tcx, ty::INNERMOST, br_inner); - let str_ref = Ty::new_imm_ref(tcx, region_inner, tcx.types.str_); + let str_ref = Ty::new_imm_ref( + tcx, + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_u32(1), + kind: ty::BoundRegionKind::Anon, + }, + ), + tcx.types.str_, + ); let slice_ty = Ty::new_slice(tcx, str_ref); - let ref_to_slice = Ty::new_imm_ref(tcx, region_outer, slice_ty); + let ref_to_slice = Ty::new_imm_ref( + tcx, + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ), + slice_ty, + ); (0, 0, vec![ref_to_slice], tcx.types.unit) } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), From bdee988198e4cee87ad4a7cbf34a58957542d416 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 9 Apr 2026 20:11:37 +0530 Subject: [PATCH 19/36] Added some more tests --- .../rustc_mir_build/src/builder/expr/into.rs | 4 +- .../intrinsics/codeview_annotation.rs | 39 +++++++++++++++++++ .../codeview_annotation_optimized.rs | 12 ++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index fbd23c4276e31..e802adfe75bf0 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -456,7 +456,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // and lower them into a CodviewAnnoation MIR intrinsic let strings = args .first() - .and_then(|&arg_id| this.extract_strings_from_array(arg_id)); + .and_then(|&arg_id| this.extract_strs_from_array(arg_id)); match strings { Some(strings) if !strings.is_empty() => { @@ -960,7 +960,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) /// to find either an inline array of string literals/named consts, or a /// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. - fn extract_strings_from_array(&self, expr_id: ExprId) -> Option> { + fn extract_strs_from_array(&self, expr_id: ExprId) -> Option> { // Peel away scopes, borrows etc. let mut id = expr_id; while let ExprKind::Scope { value, .. } diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index 13cf423c87004..950df4f7f34ea 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -35,8 +35,47 @@ pub fn macro_multiple_annotations() { std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); } +const ANNOTATION_A: &str = "const_a"; +const ANNOTATION_B: &str = "const_b"; + +// CHECK-LABEL: @named_const_elements +// CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) +#[no_mangle] +pub fn named_const_elements() { + codeview_annotation(&[ANNOTATION_A, ANNOTATION_B]); +} + +// CHECK-LABEL: @mixed_const_and_literal +// CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) +#[no_mangle] +pub fn mixed_const_and_literal() { + codeview_annotation(&[ANNOTATION_A, "literal_mix"]); +} + +const STRS_SLICE: &[&str] = &["slice_a", "slice_b"]; + +// CHECK-LABEL: @named_const_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_SLICE:![0-9]+]]) +#[no_mangle] +pub fn named_const_slice() { + codeview_annotation(STRS_SLICE); +} + +const STRS_ARRAY: [&str; 2] = ["arr_a", "arr_b"]; + +// CHECK-LABEL: @named_const_array_ref +// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_ARRAY:![0-9]+]]) +#[no_mangle] +pub fn named_const_array_ref() { + codeview_annotation(&STRS_ARRAY); +} + // Metadata definitions are at the end of LLVM IR, so check them here // CHECK-DAG: [[SINGLE]] = !{!"test_annotation"} // CHECK-DAG: [[MULTI]] = !{!"category", !"subcategory", !"details"} // CHECK-DAG: [[MACRO_SINGLE]] = !{!"macro_test"} // CHECK-DAG: [[MACRO_MULTI]] = !{!"Performance", !"HotPath", !"Critical"} +// CHECK-DAG: [[NAMED_CONST]] = !{!"const_a", !"const_b"} +// CHECK-DAG: [[MIXED]] = !{!"const_a", !"literal_mix"} +// CHECK-DAG: [[CONST_SLICE]] = !{!"slice_a", !"slice_b"} +// CHECK-DAG: [[CONST_ARRAY]] = !{!"arr_a", !"arr_b"} diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs index feb439f9dc393..8851f69391f31 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs @@ -45,7 +45,19 @@ fn annotated_helper() { codeview_annotation(&["from_inlined_fn"]); } +// Verify named const annotations survive optimization. +const OPT_STRS: &[&str] = &["const_survives_opt"]; + +// CHECK-LABEL: @named_const_optimized +// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_OPT:![0-9]+]]) +#[no_mangle] +pub fn named_const_optimized(x: u32) -> u32 { + codeview_annotation(OPT_STRS); + x.wrapping_add(42) +} + // CHECK-DAG: [[COMP]] = !{!"in_computation"} // CHECK-DAG: [[FIRST]] = !{!"first"} // CHECK-DAG: [[SECOND]] = !{!"second"} // CHECK-DAG: [[INLINED]] = !{!"from_inlined_fn"} +// CHECK-DAG: [[CONST_OPT]] = !{!"const_survives_opt"} From 98fec1aa2c9f81ed1215623da6a0b6749f78bf9f Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 09:13:53 +0530 Subject: [PATCH 20/36] Fixed formatting issues --- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 2 +- compiler/rustc_mir_build/src/builder/expr/into.rs | 5 ++--- compiler/rustc_span/src/symbol.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index ce75bd5f474d4..d9518bf9f4f44 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -86,6 +86,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ceilf32 | sym::ceilf64 | sym::ceilf128 + | sym::codeview_annotation | sym::cold_path | sym::const_eval_select | sym::contract_check_ensures @@ -102,7 +103,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ctlz | sym::ctpop | sym::cttz - | sym::codeview_annotation | sym::discriminant_value | sym::exp2f16 | sym::exp2f32 diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index e802adfe75bf0..10a3a9ced2a31 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -454,9 +454,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sym::codeview_annotation => { // Extract strings from the `&[&str]` argument of codeview_annoation // and lower them into a CodviewAnnoation MIR intrinsic - let strings = args - .first() - .and_then(|&arg_id| this.extract_strs_from_array(arg_id)); + let strings = + args.first().and_then(|&arg_id| this.extract_strs_from_array(arg_id)); match strings { Some(strings) if !strings.is_empty() => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bdfc08c334d64..d8acbfcf92204 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -618,6 +618,7 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, + codeview_annotation, coerce_pointee_validated, coerce_shared, coerce_unsized, @@ -749,7 +750,6 @@ symbols! { dead_code, dealloc, debug, - codeview_annotation, debug_assert_eq_macro, debug_assert_macro, debug_assert_ne_macro, From ae6b42899d7334a095057d1219960b96796d5e86 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 10:38:21 +0530 Subject: [PATCH 21/36] Using self.typing_env() --- compiler/rustc_mir_build/src/builder/expr/into.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 10a3a9ced2a31..a4588837c3000 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -1011,13 +1011,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ExprKind::NamedConst { def_id, args, .. } = expr.kind else { return None; }; - let instance = ty::Instance::new_raw(def_id, args); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::post_analysis(self.tcx, def_id); - let Ok(Ok(valtree)) = self.tcx.const_eval_global_id_for_typeck(typing_env, cid, expr.span) - else { + + let Ok(Ok(valtree)) = self.tcx.const_eval_global_id_for_typeck( + self.typing_env(), + GlobalId { instance: ty::Instance::new_raw(def_id, args), promoted: None }, + expr.span, + ) else { return None; }; + Self::extract_strs_from_value(self.tcx, ty::Value { ty: expr.ty, valtree }) } From 31ac7a90784c8915cbd8789523ab1fffe56df65f Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 12:10:32 +0530 Subject: [PATCH 22/36] Passing symbols to backend for lowering instead of byte arrays --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 ++-- compiler/rustc_codegen_ssa/src/mir/statement.rs | 3 +-- compiler/rustc_codegen_ssa/src/traits/intrinsic.rs | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 70d033ea3be18..ebe2e40e79876 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -717,7 +717,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn codeview_annotation(&mut self, _strings: &[&[u8]]) { + fn codeview_annotation(&mut self, _strings: &[Symbol]) { // No-op as unlike LLVM GGC has no intrinsic we can lower to } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4d9c847cbf38f..70ab2555e6524 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -835,7 +835,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } - fn codeview_annotation(&mut self, strings: &[&[u8]]) { + fn codeview_annotation(&mut self, strings: &[Symbol]) { if !self.cx.sess().target.is_like_msvc { return; } @@ -846,7 +846,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // Create MDStrings from strings let md_strings: Vec<&Metadata> = - strings.iter().map(|s| self.cx.create_metadata(s)).collect(); + strings.iter().map(|s| self.cx.create_metadata(s.as_str().as_bytes())).collect(); // Create MDTuple from all the MDStrings let md_tuple = unsafe { diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 0df9f9432969b..8d2ff085bd8cb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -97,8 +97,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation( ref symbols, )) => { - let strings: Vec<&[u8]> = symbols.iter().map(|s| s.as_str().as_bytes()).collect(); - bx.codeview_annotation(&strings); + bx.codeview_annotation(symbols); } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 1cd93b67dadea..e1576ad13a7a0 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -1,5 +1,5 @@ use rustc_middle::ty; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::BackendTypes; use crate::mir::operand::OperandRef; @@ -51,5 +51,5 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { fn va_end(&mut self, val: Self::Value) -> Self::Value; /// Emit a codeview_annotation with the given strings. - fn codeview_annotation(&mut self, strings: &[&[u8]]); + fn codeview_annotation(&mut self, strings: &[Symbol]); } From 55ab65aefb50c53ca824931e3978ea2081719b47 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 13:31:28 +0530 Subject: [PATCH 23/36] Added check for string count --- compiler/rustc_mir_transform/src/validate.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index a2119e670ad76..c6e42f955d3c4 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1540,7 +1540,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({op_cnt_ty} != usize)")) } } - StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => {} + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(symbols)) => { + if symbols.is_empty() { + self.fail(location, "`CodeviewAnnotation` must have at least one string"); + } + } StatementKind::SetDiscriminant { place, .. } => { if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); From edaf83a4fad16f5f965416dee1faa43d849162e1 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 14:36:42 +0530 Subject: [PATCH 24/36] Multiple cosmetic improvements --- .../src/polonius/legacy/loan_invalidations.rs | 1 + compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 2 +- compiler/rustc_mir_transform/src/cost_checker.rs | 2 +- src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 6 +++--- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 05c4bb88fe5ff..9fc05b4306a55 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -65,6 +65,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, dst); self.consume_operand(location, count); } + // Doesn't have any language semantics StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => {} // Only relevant for mir typeck StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index d80dd12c60f4a..7d6ae46ff3be2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -918,7 +918,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: StatementKind::Intrinsic(intrinsic) => match &**intrinsic { // We ignore `assume` intrinsics, they are only useful for optimizations NonDivergingIntrinsic::Assume(_) => {} - // CodeView annotations are only supported by the LLVM backend + // CodeView annotation is supported only by the LLVM backend NonDivergingIntrinsic::CodeviewAnnotation(_) => {} NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { src, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index b4bdc53ba7b93..9863558de0266 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -505,7 +505,7 @@ pub enum NonDivergingIntrinsic<'tcx> { /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), - /// Denotes a codeview_annotation. + /// Denotes a call to the intrinsic function `codeview_annotation`. /// /// Gets lowered to `llvm.codeview.annotation` on LLVM for MSVC targets. /// Is a no-op on other targets and backends. diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index ebc99cd2d3d3a..915b1bf9c0b29 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -87,7 +87,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += match **ndi { NonDivergingIntrinsic::Assume(..) => INSTR_COST, NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, - NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, + NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, // codeview_annotations is a no-op at runtime }; } StatementKind::Assign(..) => self.penalty += INSTR_COST, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 050a6478ae78f..193424dd05ddc 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -231,8 +231,6 @@ fn check_statement<'tcx>( StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(cx, op, span, body, msrv), - StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => Ok(()), - StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { dst, src, count }, )) => { @@ -240,8 +238,10 @@ fn check_statement<'tcx>( check_operand(cx, src, span, body, msrv)?; check_operand(cx, count, span, body, msrv) }, + // These are all NOPs - StatementKind::StorageLive(_) + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) + | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) From ce112700cda281f27c12a6dd6e7e6b301b3f9385 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 15:12:19 +0530 Subject: [PATCH 25/36] Minor comment improvements --- compiler/rustc_mir_build/src/builder/expr/into.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index a4588837c3000..b9d1e30437d1d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -955,10 +955,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Extract string from an string array expr. + /// Extract string from a string array expr. + /// /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) /// to find either an inline array of string literals/named consts, or a - /// whole NamedConst evaluating to `&[&str]` / `[&str; N]`. + /// named const evaluating to such an array. fn extract_strs_from_array(&self, expr_id: ExprId) -> Option> { // Peel away scopes, borrows etc. let mut id = expr_id; @@ -969,7 +970,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { id = value; } + + // Extract strings match self.thir[id].kind { + // Array literal: e.g. ["lit1", STR_CONST] ExprKind::Array { ref fields } => { let mut syms = Vec::with_capacity(fields.len()); for &field_id in fields.iter() { @@ -989,7 +993,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::NamedConst { .. } => { let mut strs = self.eval_named_const_strs(&self.thir[id])?; if strs.len() != 1 { - // A array field has to be a single string + // Each array field is just a single string return None; } syms.append(&mut strs); @@ -999,7 +1003,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Some(syms) } - // Array that's a named const: e.g. const STRS: &[&str] = &["lit1", "lit2"]; + // Named const array: e.g. const ARR_CONST: &[&str] = &["lit1", STR_CONST] ExprKind::NamedConst { .. } => self.eval_named_const_strs(&self.thir[id]), _ => None, } From 95e9663b34d254acda52a34aa6c1df6244b7009e Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 17:01:59 +0530 Subject: [PATCH 26/36] Removed some comments --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 70ab2555e6524..b59e755cb9a69 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -844,17 +844,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return; } - // Create MDStrings from strings let md_strings: Vec<&Metadata> = strings.iter().map(|s| self.cx.create_metadata(s.as_str().as_bytes())).collect(); - - // Create MDTuple from all the MDStrings let md_tuple = unsafe { llvm::LLVMMDNodeInContext2(self.cx.llcx, md_strings.as_ptr(), md_strings.len()) }; let md_value = self.cx.get_metadata_value(md_tuple); - - // Get the intrinsic via lookup let (fn_ty, intrinsic_fn) = self.cx.get_intrinsic("llvm.codeview.annotation".into(), &[]); self.call(fn_ty, None, None, intrinsic_fn, &[md_value], None, None); From 94b1e7ad5d25e0306f3c7ebe8ad5f1b2a6b0c4e8 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Fri, 10 Apr 2026 17:10:33 +0530 Subject: [PATCH 27/36] Fixed a few typos --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_mir_build/src/builder/expr/into.rs | 6 +++--- compiler/rustc_mir_transform/src/cost_checker.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index ebe2e40e79876..72f866cebeb37 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -718,7 +718,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } fn codeview_annotation(&mut self, _strings: &[Symbol]) { - // No-op as unlike LLVM GGC has no intrinsic we can lower to + // No-op as unlike LLVM, GCC has no intrinsic we can lower to } } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index b9d1e30437d1d..a820051a4fa60 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -370,7 +370,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Some intrinsics are handled here: // - write_via_move and write_box_via_move because they desperately want // to avoid introducing unnecessary copies. - // - codeview_annoation because it is easier to extract its argument here + // - codeview_annotation because it is easier to extract its argument here // than in codegen where it will require walking the MIR. ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() @@ -452,8 +452,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } sym::codeview_annotation => { - // Extract strings from the `&[&str]` argument of codeview_annoation - // and lower them into a CodviewAnnoation MIR intrinsic + // Extract strings from the `&[&str]` argument of codeview_annotation + // and lower them into a CodeviewAnnotation MIR intrinsic let strings = args.first().and_then(|&arg_id| this.extract_strs_from_array(arg_id)); diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 915b1bf9c0b29..48cad5bf237f8 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -87,7 +87,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += match **ndi { NonDivergingIntrinsic::Assume(..) => INSTR_COST, NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, - NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, // codeview_annotations is a no-op at runtime + NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, // codeview_annotation is a no-op at runtime }; } StatementKind::Assign(..) => self.penalty += INSTR_COST, From 6655f746c854d6694237f7783e76a9f0245d6579 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sun, 12 Apr 2026 13:28:56 +0530 Subject: [PATCH 28/36] Added more tests --- .../intrinsics/codeview_annotation.rs | 76 ++++++++++++++----- .../codeview_annotation_non_msvc.rs | 18 +++++ .../codeview_annotation_optimized.rs | 63 --------------- .../feature-gate-codeview_annotation.rs | 6 ++ .../feature-gate-codeview_annotation.stderr | 22 ++++++ tests/ui/intrinsics/codeview_annotation.rs | 70 +++++++++-------- .../ui/intrinsics/codeview_annotation.stderr | 22 ++++-- 7 files changed, 156 insertions(+), 121 deletions(-) create mode 100644 tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs delete mode 100644 tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs create mode 100644 tests/ui/feature-gates/feature-gate-codeview_annotation.rs create mode 100644 tests/ui/feature-gates/feature-gate-codeview_annotation.stderr diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index 950df4f7f34ea..d7521343692a6 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -1,4 +1,12 @@ +// Verifies that codeview_annotation intrinsic lowers correctly +// under various conditions like directly calling the intrinsic, +// calling through the macro, calling with different kinds of +// args and in the presence of duplicate calls. + //@ only-windows +//@ revisions: OPT0 OPT3 +//@ [OPT0] compile-flags: -Copt-level=0 +//@ [OPT3] compile-flags: -Copt-level=3 //@ compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] @@ -11,48 +19,49 @@ use std::intrinsics::codeview_annotation; // CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) #[no_mangle] pub fn intrinsic_single_annotation() { - codeview_annotation(&["test_annotation"]); + codeview_annotation(&["intr_single_string"]); } // CHECK-LABEL: @intrinsic_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) #[no_mangle] pub fn intrinsic_multiple_annotations() { - codeview_annotation(&["category", "subcategory", "details"]); + codeview_annotation(&["intr_multi1", "intr_multi2", "intr_multi3"]); } // CHECK-LABEL: @macro_single_annotation // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_SINGLE:![0-9]+]]) #[no_mangle] pub fn macro_single_annotation() { - std::hint::codeview_annotation!("macro_test"); + std::hint::codeview_annotation!("mac_single"); } // CHECK-LABEL: @macro_multiple_annotations // CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_MULTI:![0-9]+]]) #[no_mangle] pub fn macro_multiple_annotations() { - std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); + std::hint::codeview_annotation!("mac_multi1", "mac_multi2", "mac_multi3"); } -const ANNOTATION_A: &str = "const_a"; -const ANNOTATION_B: &str = "const_b"; +const ANNOTATION_A: &str = "named_const1"; +const ANNOTATION_B: &str = "named_const2"; +const ANNOTATION_C: &str = "named_const3"; // CHECK-LABEL: @named_const_elements // CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) #[no_mangle] pub fn named_const_elements() { - codeview_annotation(&[ANNOTATION_A, ANNOTATION_B]); + codeview_annotation(&[ANNOTATION_A, ANNOTATION_B, ANNOTATION_C]); } -// CHECK-LABEL: @mixed_const_and_literal +// CHECK-LABEL: @mixed_named_const_and_literal_elements // CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) #[no_mangle] -pub fn mixed_const_and_literal() { - codeview_annotation(&[ANNOTATION_A, "literal_mix"]); +pub fn mixed_named_const_and_literal_elements() { + codeview_annotation(&[ANNOTATION_A, "mixed_literal1", "mixed_literal2"]); } -const STRS_SLICE: &[&str] = &["slice_a", "slice_b"]; +const STRS_SLICE: &[&str] = &["slice_element1", "slice_element2", "slice_element3"]; // CHECK-LABEL: @named_const_slice // CHECK: call void @llvm.codeview.annotation(metadata [[CONST_SLICE:![0-9]+]]) @@ -61,7 +70,7 @@ pub fn named_const_slice() { codeview_annotation(STRS_SLICE); } -const STRS_ARRAY: [&str; 2] = ["arr_a", "arr_b"]; +const STRS_ARRAY: [&str; 3] = ["arr_element1", "arr_element2", "arr_element3"]; // CHECK-LABEL: @named_const_array_ref // CHECK: call void @llvm.codeview.annotation(metadata [[CONST_ARRAY:![0-9]+]]) @@ -70,12 +79,39 @@ pub fn named_const_array_ref() { codeview_annotation(&STRS_ARRAY); } +// Multiple annotations with same strings within a single function +// CHECK-LABEL: @duplicate_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[DUP:![0-9]+]]) +// CHECK: call void @llvm.codeview.annotation(metadata [[DUP]]) +#[no_mangle] +pub fn duplicate_annotations() { + codeview_annotation(&["dup1", "dup2", "dup3"]); + codeview_annotation(&["dup1", "dup2", "dup3"]); +} + +// Multiple annotations with same strings within different functions +// CHECK-LABEL: @duplicate_annotations_func_a +// CHECK: call void @llvm.codeview.annotation(metadata [[FUNC_DUP:![0-9]+]]) +#[no_mangle] +pub fn duplicate_annotations_func_a() { + codeview_annotation(&["func_dup1", "func_dup2", "func_dup3"]); +} + +// CHECK-LABEL: @duplicate_annotations_func_b +// CHECK: call void @llvm.codeview.annotation(metadata [[FUNC_DUP:![0-9]+]]) +#[no_mangle] +pub fn duplicate_annotations_func_b() { + codeview_annotation(&["func_dup1", "func_dup2", "func_dup3"]); +} + // Metadata definitions are at the end of LLVM IR, so check them here -// CHECK-DAG: [[SINGLE]] = !{!"test_annotation"} -// CHECK-DAG: [[MULTI]] = !{!"category", !"subcategory", !"details"} -// CHECK-DAG: [[MACRO_SINGLE]] = !{!"macro_test"} -// CHECK-DAG: [[MACRO_MULTI]] = !{!"Performance", !"HotPath", !"Critical"} -// CHECK-DAG: [[NAMED_CONST]] = !{!"const_a", !"const_b"} -// CHECK-DAG: [[MIXED]] = !{!"const_a", !"literal_mix"} -// CHECK-DAG: [[CONST_SLICE]] = !{!"slice_a", !"slice_b"} -// CHECK-DAG: [[CONST_ARRAY]] = !{!"arr_a", !"arr_b"} +// CHECK-DAG: [[SINGLE]] = !{!"intr_single_string"} +// CHECK-DAG: [[MULTI]] = !{!"intr_multi1", !"intr_multi2", !"intr_multi3"} +// CHECK-DAG: [[MACRO_SINGLE]] = !{!"mac_single"} +// CHECK-DAG: [[MACRO_MULTI]] = !{!"mac_multi1", !"mac_multi2", !"mac_multi3"} +// CHECK-DAG: [[NAMED_CONST]] = !{!"named_const1", !"named_const2", !"named_const3"} +// CHECK-DAG: [[MIXED]] = !{!"named_const1", !"mixed_literal1", !"mixed_literal2"} +// CHECK-DAG: [[CONST_SLICE]] = !{!"slice_element1", !"slice_element2", !"slice_element3"} +// CHECK-DAG: [[CONST_ARRAY]] = !{!"arr_element1", !"arr_element2", !"arr_element3"} +// CHECK-DAG: [[DUP]] = !{!"dup1", !"dup2", !"dup3"} +// CHECK-DAG: [[FUNC_DUP]] = !{!"func_dup1", !"func_dup2", !"func_dup3"} diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs new file mode 100644 index 0000000000000..23f149ed0e801 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs @@ -0,0 +1,18 @@ +// Verifies that codeview_annotation does NOT emit `llvm.codeview.annotation` +// on non-MSVC targets. The intrinsic should be silently ignored. + +//@ ignore-msvc +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// CHECK-LABEL: @test_non_msvc +// CHECK-NOT: codeview.annotation +#[no_mangle] +pub fn test_non_msvc() { + codeview_annotation(&["string1, string2, string3"]); +} diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs deleted file mode 100644 index 8851f69391f31..0000000000000 --- a/tests/codegen-llvm/intrinsics/codeview_annotation_optimized.rs +++ /dev/null @@ -1,63 +0,0 @@ -//@ only-windows -//@ compile-flags: -Copt-level=2 -// -// Verify that codeview_annotations survive LLVM optimizations. -// The llvm.codeview.annotation intrinsic is marked noduplicate and -// writes to inaccessible memory, so LLVM must not remove it. - -#![crate_type = "lib"] -#![feature(codeview_annotation)] -#![feature(core_intrinsics)] - -use std::intrinsics::codeview_annotation; - -// Verify that an annotation inside a function with other code is not -// removed even when surrounding code is optimized. -// CHECK-LABEL: @annotation_with_computation -// CHECK: call void @llvm.codeview.annotation(metadata [[COMP:![0-9]+]]) -#[no_mangle] -pub fn annotation_with_computation(x: u32) -> u32 { - codeview_annotation(&["in_computation"]); - x.wrapping_mul(31).wrapping_add(17) -} - -// Verify that multiple annotations in the same function are all preserved. -// CHECK-LABEL: @multiple_annotations_same_fn -// CHECK: call void @llvm.codeview.annotation(metadata [[FIRST:![0-9]+]]) -// CHECK: call void @llvm.codeview.annotation(metadata [[SECOND:![0-9]+]]) -#[no_mangle] -pub fn multiple_annotations_same_fn() { - codeview_annotation(&["first"]); - codeview_annotation(&["second"]); -} - -// Verify that annotations survive inlining: the annotation from -// the inlined callee should appear in the caller. -// CHECK-LABEL: @annotation_after_inlining -// CHECK: call void @llvm.codeview.annotation(metadata [[INLINED:![0-9]+]]) -#[no_mangle] -pub fn annotation_after_inlining() { - annotated_helper(); -} - -#[inline(always)] -fn annotated_helper() { - codeview_annotation(&["from_inlined_fn"]); -} - -// Verify named const annotations survive optimization. -const OPT_STRS: &[&str] = &["const_survives_opt"]; - -// CHECK-LABEL: @named_const_optimized -// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_OPT:![0-9]+]]) -#[no_mangle] -pub fn named_const_optimized(x: u32) -> u32 { - codeview_annotation(OPT_STRS); - x.wrapping_add(42) -} - -// CHECK-DAG: [[COMP]] = !{!"in_computation"} -// CHECK-DAG: [[FIRST]] = !{!"first"} -// CHECK-DAG: [[SECOND]] = !{!"second"} -// CHECK-DAG: [[INLINED]] = !{!"from_inlined_fn"} -// CHECK-DAG: [[CONST_OPT]] = !{!"const_survives_opt"} diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.rs b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs new file mode 100644 index 0000000000000..985c63b04a501 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs @@ -0,0 +1,6 @@ +//@ only-windows + +fn main() { + std::hint::codeview_annotation!("string1, string2, string3"); //~ ERROR use of unstable library feature `codeview_annotation` + //~| ERROR use of unstable library feature `codeview_annotation` +} diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr new file mode 100644 index 0000000000000..238a1902f5d79 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -0,0 +1,22 @@ +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:6:5 + | +LL | std::hint::codeview_annotation!("string1, string2, string3"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:6:5 + | +LL | std::hint::codeview_annotation!("string1, string2, string3"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `std::hint::codeview_annotation` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 993ae68a150c7..7e0455b352ae7 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -1,84 +1,88 @@ +// Verifies codeview_annotation compile-time behavior: +// - Happy paths: inline literals, macro usage, named const elements, +// mixed const/literal, const slices, const array refs, and const fn usage. +// - Error cases: non-const arguments, function parameters, empty arrays, +// and wrong types. //@ only-windows -// Verify codeview_annotation behavior: happy paths and error cases. #![feature(codeview_annotation)] #![feature(core_intrinsics)] use std::intrinsics::codeview_annotation; -// Single annotation via intrinsic fn intrinsic_single() { - codeview_annotation(&["test_annotation"]); + codeview_annotation(&["string1"]); } -// Multiple annotations via intrinsic fn intrinsic_multiple() { - codeview_annotation(&["category", "subcategory", "details"]); + codeview_annotation(&["string1", "string2", "string3"]); } -// Single annotation via macro fn macro_single() { - std::hint::codeview_annotation!("macro_test"); + std::hint::codeview_annotation!("string1"); } -// Multiple annotations via macro fn macro_multiple() { - std::hint::codeview_annotation!("Performance", "HotPath", "Critical"); + std::hint::codeview_annotation!("string1", "string2", "string3"); } -// Named const elements -const ANNOTATION_A: &str = "hello"; -const ANNOTATION_B: &str = "there"; - +const ANNOTATION_A: &str = "string1"; +const ANNOTATION_B: &str = "string2"; +const ANNOTATION_C: &str = "string3"; fn named_const_elements() { - codeview_annotation(&[ANNOTATION_A, ANNOTATION_B]); + codeview_annotation(&[ANNOTATION_A, ANNOTATION_B, ANNOTATION_C]); } -// Named const and literal -fn mixed_const_and_literal() { - codeview_annotation(&[ANNOTATION_A, "world"]); +fn mixed_named_const_and_literal_elements() { + codeview_annotation(&[ANNOTATION_A, "string2", "string3"]); } -// Const slice -const STRS_SLICE: &[&str] = &["hello", "there"]; - +const STRS_SLICE: &[&str] = &["string1", "string2", "string3"]; fn named_const_slice() { codeview_annotation(STRS_SLICE); } -// Ref to named const array -const STRS_ARRAY: [&str; 2] = ["hello", "there"]; - +const STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; fn named_const_array_ref() { codeview_annotation(&STRS_ARRAY); } -// Error case: local variable -fn non_const_arg() { - let s = "hello"; - codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a constant argument +// Use in const function +const fn annotated_computation(x: u32) -> u32 { + codeview_annotation(&["string1", "string2", "string3"]); + x + 1 } -// Error case: function parameter -fn fn_param_arg(strs: &[&str]) { +// --- Error cases --- + +fn non_const_arg(strs: &[&str]) { codeview_annotation(strs); //~ ERROR codeview_annotation requires a constant argument + let s = "string1"; + codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a constant argument } -// Error case: empty array fn empty_array() { codeview_annotation(&[]); //~ ERROR codeview_annotation requires a non-empty array argument } +fn wrong_type() { + codeview_annotation(42); //~ ERROR mismatched types +} + + fn main() { intrinsic_single(); intrinsic_multiple(); macro_single(); macro_multiple(); named_const_elements(); - mixed_const_and_literal(); + mixed_named_const_and_literal_elements(); named_const_slice(); named_const_array_ref(); - non_const_arg(); - fn_param_arg(&["a"]); + let _ = annotated_computation(5); + const _: u32 = annotated_computation(5); + + non_const_arg(&["a"]); empty_array(); + wrong_type(); } diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index cb185787b2847..22c3490f689ff 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -1,20 +1,32 @@ +error[E0308]: mismatched types + --> $DIR/codeview_annotation.rs:69:25 + | +LL | codeview_annotation(42); + | ------------------- ^^ expected `&[&str]`, found integer + | | + | arguments to this function are incorrect + | +note: function defined here + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + error: codeview_annotation requires a constant argument --> $DIR/codeview_annotation.rs:59:5 | -LL | codeview_annotation(&[s]); +LL | codeview_annotation(strs); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: codeview_annotation requires a constant argument - --> $DIR/codeview_annotation.rs:64:5 + --> $DIR/codeview_annotation.rs:61:5 | -LL | codeview_annotation(strs); +LL | codeview_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: codeview_annotation requires a non-empty array argument - --> $DIR/codeview_annotation.rs:69:5 + --> $DIR/codeview_annotation.rs:65:5 | LL | codeview_annotation(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0308`. From dd705e1faddcdbf6a9e04aa8c4c0a50096769820 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sun, 12 Apr 2026 14:13:38 +0530 Subject: [PATCH 29/36] A few more fixes --- compiler/rustc_mir_build/src/builder/expr/into.rs | 3 ++- tests/codegen-llvm/intrinsics/codeview_annotation.rs | 2 +- .../feature-gate-codeview_annotation.rs | 4 +--- .../feature-gate-codeview_annotation.stderr | 10 +++++----- tests/ui/intrinsics/codeview_annotation.rs | 6 ++---- tests/ui/intrinsics/codeview_annotation.stderr | 12 ++++++------ 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index a820051a4fa60..bc42b5491326a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -480,7 +480,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None => { this.tcx.dcx().span_err( expr_span, - "codeview_annotation requires a constant argument", + "codeview_annotation requires a const string array argument", ); } } @@ -980,6 +980,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut id = field_id; while let ExprKind::Scope { value, .. } | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } | ExprKind::Deref { arg: value } = self.thir[id].kind { id = value; diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index d7521343692a6..a0dfc718dff5d 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -3,7 +3,7 @@ // calling through the macro, calling with different kinds of // args and in the presence of duplicate calls. -//@ only-windows +//@ only-msvc //@ revisions: OPT0 OPT3 //@ [OPT0] compile-flags: -Copt-level=0 //@ [OPT3] compile-flags: -Copt-level=3 diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.rs b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs index 985c63b04a501..bfeaf5339c8e9 100644 --- a/tests/ui/feature-gates/feature-gate-codeview_annotation.rs +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs @@ -1,6 +1,4 @@ -//@ only-windows - fn main() { - std::hint::codeview_annotation!("string1, string2, string3"); //~ ERROR use of unstable library feature `codeview_annotation` + std::hint::codeview_annotation!("string1", "string2", "string3"); //~ ERROR use of unstable library feature `codeview_annotation` //~| ERROR use of unstable library feature `codeview_annotation` } diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr index 238a1902f5d79..c254b8da01c87 100644 --- a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -1,17 +1,17 @@ error[E0658]: use of unstable library feature `codeview_annotation` - --> $DIR/feature-gate-codeview_annotation.rs:6:5 + --> $DIR/feature-gate-codeview_annotation.rs:2:5 | -LL | std::hint::codeview_annotation!("string1, string2, string3"); +LL | std::hint::codeview_annotation!("string1", "string2", "string3"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `codeview_annotation` - --> $DIR/feature-gate-codeview_annotation.rs:6:5 + --> $DIR/feature-gate-codeview_annotation.rs:2:5 | -LL | std::hint::codeview_annotation!("string1, string2, string3"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | std::hint::codeview_annotation!("string1", "string2", "string3"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 7e0455b352ae7..fa10de53b96e7 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -3,8 +3,6 @@ // mixed const/literal, const slices, const array refs, and const fn usage. // - Error cases: non-const arguments, function parameters, empty arrays, // and wrong types. -//@ only-windows - #![feature(codeview_annotation)] #![feature(core_intrinsics)] @@ -56,9 +54,9 @@ const fn annotated_computation(x: u32) -> u32 { // --- Error cases --- fn non_const_arg(strs: &[&str]) { - codeview_annotation(strs); //~ ERROR codeview_annotation requires a constant argument + codeview_annotation(strs); //~ ERROR codeview_annotation requires a const string array argument let s = "string1"; - codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a constant argument + codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a const string array argument } fn empty_array() { diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index 22c3490f689ff..06168e9b03b00 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/codeview_annotation.rs:69:25 + --> $DIR/codeview_annotation.rs:67:25 | LL | codeview_annotation(42); | ------------------- ^^ expected `&[&str]`, found integer @@ -9,20 +9,20 @@ LL | codeview_annotation(42); note: function defined here --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error: codeview_annotation requires a constant argument - --> $DIR/codeview_annotation.rs:59:5 +error: codeview_annotation requires a const string array argument + --> $DIR/codeview_annotation.rs:57:5 | LL | codeview_annotation(strs); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires a constant argument - --> $DIR/codeview_annotation.rs:61:5 +error: codeview_annotation requires a const string array argument + --> $DIR/codeview_annotation.rs:59:5 | LL | codeview_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: codeview_annotation requires a non-empty array argument - --> $DIR/codeview_annotation.rs:65:5 + --> $DIR/codeview_annotation.rs:63:5 | LL | codeview_annotation(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^ From 1c6f36e37e19fea111ec5fb407cd674f4a20157c Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 10:36:49 +0530 Subject: [PATCH 30/36] Removed a stray underscore --- library/core/src/intrinsics/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 777e595d5800a..d37ba87122a6c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -345,7 +345,7 @@ pub fn breakpoint(); #[unstable(feature = "codeview_annotation", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -pub fn codeview_annotation(_strings: &[&str]); +pub fn codeview_annotation(strings: &[&str]); /// Magic intrinsic that derives its meaning from attributes /// attached to the function. From fbb804a95250759f59c242b3f4a8d41f6b9f5c7f Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 11:29:41 +0530 Subject: [PATCH 31/36] Improved error messages --- compiler/rustc_mir_build/src/builder/expr/into.rs | 6 +++--- .../feature-gates/feature-gate-codeview_annotation.stderr | 2 +- tests/ui/intrinsics/codeview_annotation.rs | 6 +++--- tests/ui/intrinsics/codeview_annotation.stderr | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index bc42b5491326a..ba6bb9e9ec8e8 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -474,13 +474,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(_) => { this.tcx.dcx().span_err( expr_span, - "codeview_annotation requires a non-empty array argument", + "`codeview_annotation` expects a non-empty array", ); } None => { this.tcx.dcx().span_err( expr_span, - "codeview_annotation requires a const string array argument", + "`codeview_annotation` expects a const array", ); } } @@ -955,7 +955,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Extract string from a string array expr. + /// Extract strings from a string array expr. /// /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) /// to find either an inline array of string literals/named consts, or a diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr index c254b8da01c87..e8e855f200787 100644 --- a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -11,7 +11,7 @@ error[E0658]: use of unstable library feature `codeview_annotation` --> $DIR/feature-gate-codeview_annotation.rs:2:5 | LL | std::hint::codeview_annotation!("string1", "string2", "string3"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index fa10de53b96e7..56f08112d2d1a 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -54,13 +54,13 @@ const fn annotated_computation(x: u32) -> u32 { // --- Error cases --- fn non_const_arg(strs: &[&str]) { - codeview_annotation(strs); //~ ERROR codeview_annotation requires a const string array argument + codeview_annotation(strs); //~ ERROR `codeview_annotation` expects a const array let s = "string1"; - codeview_annotation(&[s]); //~ ERROR codeview_annotation requires a const string array argument + codeview_annotation(&[s]); //~ ERROR `codeview_annotation` expects a const array } fn empty_array() { - codeview_annotation(&[]); //~ ERROR codeview_annotation requires a non-empty array argument + codeview_annotation(&[]); //~ ERROR `codeview_annotation` expects a non-empty array } fn wrong_type() { diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index 06168e9b03b00..3900828f882b7 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -9,19 +9,19 @@ LL | codeview_annotation(42); note: function defined here --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error: codeview_annotation requires a const string array argument +error: `codeview_annotation` expects a const array --> $DIR/codeview_annotation.rs:57:5 | LL | codeview_annotation(strs); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires a const string array argument +error: `codeview_annotation` expects a const array --> $DIR/codeview_annotation.rs:59:5 | LL | codeview_annotation(&[s]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: codeview_annotation requires a non-empty array argument +error: `codeview_annotation` expects a non-empty array --> $DIR/codeview_annotation.rs:63:5 | LL | codeview_annotation(&[]); From b77319f3767db1b8a41e64bd25809c44f0914ed1 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 13:30:32 +0530 Subject: [PATCH 32/36] Improvements in comments --- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7d6ae46ff3be2..4382230b0134a 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -918,7 +918,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: StatementKind::Intrinsic(intrinsic) => match &**intrinsic { // We ignore `assume` intrinsics, they are only useful for optimizations NonDivergingIntrinsic::Assume(_) => {} - // CodeView annotation is supported only by the LLVM backend + // codeview_annotation is supported only by the LLVM backend NonDivergingIntrinsic::CodeviewAnnotation(_) => {} NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { src, diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 72f866cebeb37..52e8edc9a0006 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -718,7 +718,8 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } fn codeview_annotation(&mut self, _strings: &[Symbol]) { - // No-op as unlike LLVM, GCC has no intrinsic we can lower to + // No-op as codeview_annotation is supported only by the LLVM backend. + // GCC has no intrinsic we can lower to } } From c4b77c2c4f094562953904477d4af4293ebe268f Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 14:33:15 +0530 Subject: [PATCH 33/36] Fixed a mistake in args in a test --- tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs index 23f149ed0e801..fe3f72f9eb3a0 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs @@ -14,5 +14,5 @@ use std::intrinsics::codeview_annotation; // CHECK-NOT: codeview.annotation #[no_mangle] pub fn test_non_msvc() { - codeview_annotation(&["string1, string2, string3"]); + codeview_annotation(&["string1", "string2", "string3"]); } From b4d7a415a790905a5a478eda1afe5fe7396c721e Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 14:58:06 +0530 Subject: [PATCH 34/36] Renamed some consts --- tests/codegen-llvm/intrinsics/codeview_annotation.rs | 10 +++++----- tests/ui/intrinsics/codeview_annotation.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs index a0dfc718dff5d..01b4e8687ac40 100644 --- a/tests/codegen-llvm/intrinsics/codeview_annotation.rs +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -43,22 +43,22 @@ pub fn macro_multiple_annotations() { std::hint::codeview_annotation!("mac_multi1", "mac_multi2", "mac_multi3"); } -const ANNOTATION_A: &str = "named_const1"; -const ANNOTATION_B: &str = "named_const2"; -const ANNOTATION_C: &str = "named_const3"; +const STR_A: &str = "named_const1"; +const STR_B: &str = "named_const2"; +const STR_C: &str = "named_const3"; // CHECK-LABEL: @named_const_elements // CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) #[no_mangle] pub fn named_const_elements() { - codeview_annotation(&[ANNOTATION_A, ANNOTATION_B, ANNOTATION_C]); + codeview_annotation(&[STR_A, STR_B, STR_C]); } // CHECK-LABEL: @mixed_named_const_and_literal_elements // CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) #[no_mangle] pub fn mixed_named_const_and_literal_elements() { - codeview_annotation(&[ANNOTATION_A, "mixed_literal1", "mixed_literal2"]); + codeview_annotation(&[STR_A, "mixed_literal1", "mixed_literal2"]); } const STRS_SLICE: &[&str] = &["slice_element1", "slice_element2", "slice_element3"]; diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 56f08112d2d1a..3eb16c69d0e85 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -24,15 +24,15 @@ fn macro_multiple() { std::hint::codeview_annotation!("string1", "string2", "string3"); } -const ANNOTATION_A: &str = "string1"; -const ANNOTATION_B: &str = "string2"; -const ANNOTATION_C: &str = "string3"; +const STR_A: &str = "string1"; +const STR_B: &str = "string2"; +const STR_C: &str = "string3"; fn named_const_elements() { - codeview_annotation(&[ANNOTATION_A, ANNOTATION_B, ANNOTATION_C]); + codeview_annotation(&[STR_A, STR_B, STR_C]); } fn mixed_named_const_and_literal_elements() { - codeview_annotation(&[ANNOTATION_A, "string2", "string3"]); + codeview_annotation(&[STR_A, "string2", "string3"]); } const STRS_SLICE: &[&str] = &["string1", "string2", "string3"]; From 77c182e08965ebbae32ecfc735e7ab0663fd0915 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 Apr 2026 15:05:26 +0530 Subject: [PATCH 35/36] Added a bug() for empty strings in codegen --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b59e755cb9a69..088c88988ccab 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -841,7 +841,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } if strings.is_empty() { - return; + bug!("codeview_annotation with empty strings should not reach codegen"); } let md_strings: Vec<&Metadata> = From 671f701d98d14e3d778adce4431f741a13174f98 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sat, 25 Apr 2026 14:02:28 +0530 Subject: [PATCH 36/36] Rejecting consts with generic params --- compiler/rustc_mir_build/src/builder/expr/into.rs | 7 ++++++- tests/ui/intrinsics/codeview_annotation.rs | 13 +++++++++++++ tests/ui/intrinsics/codeview_annotation.stderr | 8 +++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index ba6bb9e9ec8e8..b26901b47a065 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::interpret::GlobalId; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::thir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{DUMMY_SP, Spanned, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; @@ -1017,6 +1017,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return None; }; + // Can't evaluate consts with unresolved generic params (e.g. T::NAME) + if args.has_non_region_param() { + return None; + } + let Ok(Ok(valtree)) = self.tcx.const_eval_global_id_for_typeck( self.typing_env(), GlobalId { instance: ty::Instance::new_raw(def_id, args), promoted: None }, diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs index 3eb16c69d0e85..f54ec017ca0ec 100644 --- a/tests/ui/intrinsics/codeview_annotation.rs +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -67,6 +67,18 @@ fn wrong_type() { codeview_annotation(42); //~ ERROR mismatched types } +trait TypeName { + const NAME: &str; +} + +impl TypeName for i32 { + const NAME: &str = "i32"; +} + +fn generics() { + codeview_annotation(&[T::NAME]); //~ ERROR `codeview_annotation` expects a const array +} + fn main() { intrinsic_single(); @@ -83,4 +95,5 @@ fn main() { non_const_arg(&["a"]); empty_array(); wrong_type(); + generics::(); } diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr index 3900828f882b7..aa9e2b620b683 100644 --- a/tests/ui/intrinsics/codeview_annotation.stderr +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -27,6 +27,12 @@ error: `codeview_annotation` expects a non-empty array LL | codeview_annotation(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: `codeview_annotation` expects a const array + --> $DIR/codeview_annotation.rs:79:5 + | +LL | codeview_annotation(&[T::NAME]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0308`.