diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 4a779e22870f6..56e3eb8e6777c 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -535,6 +535,9 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().debugtrap(); } + sym::codeview_annotation => { + intrinsic_args!(fx, args => (); intrinsic); + } sym::copy => { intrinsic_args!(fx, args => (src, dst, count); intrinsic); let src = src.load_scalar(fx); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index b3b1bea68e0b4..090ff1f427a3e 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -360,6 +360,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::breakpoint => { unimplemented!(); } + sym::codeview_annotation => { + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } sym::va_arg => { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ddc57af0a56e7..6cf4e9618581a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,8 @@ use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::find_attr; use rustc_lint_defs::builtin::DEPRECATED_LLVM_INTRINSIC; -use rustc_middle::mir::BinOp; +use rustc_middle::mir::interpret::{AllocId, Allocation, ErrorHandled, GlobalAlloc, alloc_range}; +use rustc_middle::mir::{self, BinOp}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::offload_meta::OffloadMetadata; use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv}; @@ -46,7 +47,7 @@ use crate::diagnostics::{ OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::intrinsic::ty::typetree::fnc_typetrees; -use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; +use crate::llvm::{self, Attribute, AttributePlace, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -894,6 +895,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } + sym::codeview_annotation => { + if self.sess().target.uses_pdb_debuginfo() { + codegen_codeview_annotation(self, instance, span); + } + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } + _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code @@ -3417,3 +3425,141 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } + +fn codegen_codeview_annotation<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + instance: ty::Instance<'tcx>, + span: Span, +) { + // This function lowers the `codeview_annotation` intrinsic, + // which is declared like this: + // + // trait CodeViewAnnotationArgs { + // const ARGS: &[&str]; + // } + // + // fn codeview_annotation() {} + // + // It finds the `T: CodeViewAnnotationArgs` trait bound, locates + // `ARGS`, const evaluates it, reads the strings off the resulting + // allocation and lowers them. + + const ARGS_CONST_NAME: &str = "`CodeViewAnnotationArgs::ARGS`"; + + let tcx = bx.tcx(); + + // Locate `ARGS` + let Some((args_const_def_id, generic_args)) = tcx + .explicit_clauses_of(instance.def_id()) + .instantiate_own(tcx, instance.args) + .filter_map(|(clause, _)| clause.skip_norm_wip().as_trait_clause()) + .filter_map(|trait_clause| { + let trait_ref = trait_clause.skip_binder().trait_ref; + tcx.associated_items(trait_ref.def_id) + .in_definition_order() + .find(|item| { + item.tag() == ty::AssocTag::Const + && tcx.item_name(item.def_id).as_str() == "ARGS" + }) + .map(|item| (item.def_id, trait_ref.args)) + }) + .next() + else { + span_bug!(span, "could not find {ARGS_CONST_NAME}"); + }; + + // Const evaluate `ARGS` + let args_const_val = match tcx.const_eval_resolve( + bx.typing_env(), + mir::UnevaluatedConst::new(args_const_def_id, generic_args), + span, + ) { + Ok(val) => val, + Err(ErrorHandled::Reported(..)) => return, + Err(ErrorHandled::TooGeneric(_)) => { + span_bug!(span, "{ARGS_CONST_NAME} is too generic") + } + }; + + // Read the strings + let array_alloc = match args_const_val { + mir::ConstValue::Slice { alloc_id, meta } => Some((alloc_id, Size::ZERO, meta)), + mir::ConstValue::Indirect { alloc_id, offset } => read_slice_pointer(tcx, alloc_id, offset) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} slice pointer")), + _ => span_bug!(span, "unexpected {ARGS_CONST_NAME} value: {args_const_val:?}"), + }; + + let strings = array_alloc.into_iter().flat_map(|(array_alloc_id, array_offset, array_len)| { + (0..array_len).map(move |index| { + let elem_size = tcx.data_layout.pointer_size().bytes() * 2; // Multiplying by 2 for fat pointers + let elem_offset = array_offset + Size::from_bytes(index * elem_size); + + let (string_alloc_id, string_offset, string_len) = + match read_slice_pointer(tcx, array_alloc_id, elem_offset) { + Ok(Some(slice)) => slice, + Ok(None) => return &[] as &[u8], + Err(()) => span_bug!(span, "invalid {ARGS_CONST_NAME} string pointer"), + }; + + resolve_alloc(tcx, string_alloc_id) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} string allocation")) + .get_bytes_strip_provenance( + &tcx, + alloc_range(string_offset, Size::from_bytes(string_len)), + ) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} string bytes")) + }) + }); + + // Lower the strings + let md_strings: Vec<&Metadata> = strings.map(|s| bx.cx.create_metadata(s)).collect(); + let md_tuple = bx.cx.md_node_in_context(&md_strings); + let md_value = bx.cx.get_metadata_value(md_tuple); + let (fn_ty, intrinsic_fn) = bx.cx.get_intrinsic("llvm.codeview.annotation".into(), &[]); + + bx.call(fn_ty, None, None, intrinsic_fn, &[md_value], None, None); +} + +// Reads the slice pointer stored at `offset` in `alloc_id` and +// returns its pointee's allocation ID, relative byte offset +// and slice length. Returns `None` for an empty slice. +fn read_slice_pointer<'tcx>( + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, + offset: Size, +) -> Result, ()> { + let alloc = resolve_alloc(tcx, alloc_id)?; + let pointer_size = tcx.data_layout.pointer_size(); + + let pointer = alloc + .read_scalar(&tcx, alloc_range(offset, pointer_size), true) + .map_err(|_| ())? + .to_pointer(&tcx); + + let len = alloc + .read_scalar(&tcx, alloc_range(offset + pointer_size, pointer_size), false) + .map_err(|_| ())? + .to_target_usize(&tcx) + .discard_err() + .ok_or(())?; + + if len == 0 { + return Ok(None); + } + + let (provenance, offset) = + pointer.into_pointer_or_addr().map_err(|_| ())?.prov_and_relative_offset(); + + Ok(Some((provenance.alloc_id(), offset, len))) +} + +fn resolve_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId) -> Result<&'tcx Allocation, ()> { + let alloc = match tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => alloc, + GlobalAlloc::Static(def_id) => tcx.eval_static_initializer(def_id).map_err(|_| ())?, + _ => return Err(()), + } + .inner(); + + Ok(alloc) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 5964f5b858ac0..470fe36f3559a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -126,6 +126,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::cold_path | sym::gpu_launch_sized_workgroup_mem | sym::breakpoint + | sym::codeview_annotation | sym::amdgpu_dispatch_ptr | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 0d7ff905300cc..97f92b55d05c8 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 @@ -306,6 +307,7 @@ 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 => (1, 0, vec![], 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 6376fe032c64e..128b643ef4666 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -635,6 +635,7 @@ symbols! { cmpxchg16b_target_feature, cmse_nonsecure_entry, code, + codeview_annotation, coerce_pointee_validated, coerce_shared, coerce_shared_target, diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 90326e649058b..de86c02142eed 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1028,3 +1028,41 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), } } + +/// A container for the string arguments passed to [`codeview_annotation`]. +#[unstable(feature = "codeview_annotation", issue = "none")] +pub trait CodeViewAnnotationArgs { + /// The string arguments to `codeview_annotation`. + const ARGS: &[&str]; +} + +/// Writes [`T::ARGS`](CodeViewAnnotationArgs::ARGS) to the PDB as an `S_ANNOTATION` record using +/// [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic). +/// +/// This function works only on targets that use PDB debug information (such as `msvc`) and the LLVM +/// backend. It is a no-op on other targets and backends. +/// +/// # Examples +/// +/// To call `codeview_annotation`, the caller must declare a type +/// implementing [`CodeViewAnnotationArgs`] and pass it as the type +/// parameter to `codeview_annotation`. The string arguments must be +/// specified in `CodeViewAnnotationArgs::ARGS`. +/// +/// ``` +/// #![feature(codeview_annotation)] +/// use std::hint::{codeview_annotation, CodeViewAnnotationArgs}; +/// +/// struct Args; +/// +/// impl CodeViewAnnotationArgs for Args { +/// const ARGS: &[&str] = &["Hello", "World"]; +/// } +/// +/// codeview_annotation::(); +/// ``` +#[inline(always)] +#[unstable(feature = "codeview_annotation", issue = "none")] +pub fn codeview_annotation() { + crate::intrinsics::codeview_annotation::(); +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 673454abaf04f..0d9054a25ef6c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -360,6 +360,18 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in [`crate::hint::CodeViewAnnotationArgs::ARGS`] being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with targets that use PDB debuginfo and with the LLVM backend. +/// Is a no-op on other targets and backends. +#[unstable(feature = "codeview_annotation", issue = "none")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[miri::intrinsic_fallback_is_spec] +pub fn codeview_annotation() {} + /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// diff --git a/src/tools/miri/tests/pass/codeview-annotation.rs b/src/tools/miri/tests/pass/codeview-annotation.rs new file mode 100644 index 0000000000000..0d0e550e026a7 --- /dev/null +++ b/src/tools/miri/tests/pass/codeview-annotation.rs @@ -0,0 +1,16 @@ +// Verifies that `codeview_annotation` is a no-op under Miri +// and that Miri does not try to const eval `CodeViewAnnotationArgs::ARGS` + +#![feature(codeview_annotation)] + +use std::hint::{CodeViewAnnotationArgs, codeview_annotation}; + +struct Args; + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = panic!("Panic triggered if Miri tries to evaluate annotation args"); +} + +fn main() { + codeview_annotation::(); +} diff --git a/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs b/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs new file mode 100644 index 0000000000000..d0f10f14965dc --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs @@ -0,0 +1,23 @@ +// Verifies that `codeview_annotation` does NOT emit `llvm.codeview.annotation` +// on targets not using PDB debuginfo which is everything except MSVC and UEFI. + +//@ ignore-msvc +//@ ignore-uefi +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +use std::hint::{CodeViewAnnotationArgs, codeview_annotation}; + +struct Args; + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &["string1", "string2", "string3"]; +} + +// CHECK-LABEL: @test_non_pdb +// CHECK-NOT: llvm.codeview.annotation +#[no_mangle] +pub fn test_non_pdb() { + codeview_annotation::(); +} diff --git a/tests/codegen-llvm/intrinsics/codeview-annotation.rs b/tests/codegen-llvm/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..de4c4388ec28f --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview-annotation.rs @@ -0,0 +1,197 @@ +// Verifies that codeview_annotation lowers correctly to +// `llvm.codeview.annotation` + +//@ only-msvc +//@ revisions: OPT0 OPT3 +//@ [OPT0] compile-flags: -Copt-level=0 +//@ [OPT3] compile-flags: -Copt-level=3 +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +// === Helper macros === +macro_rules! call_intrinsic { + ($args:expr) => {{ + call_codeview_annotation!(std::intrinsics, $args); + }}; +} + +macro_rules! call_api { + ($args:expr) => {{ + call_codeview_annotation!(std::hint, $args); + }}; +} + +macro_rules! call_codeview_annotation { + ($($module:ident)::+, $args:expr) => {{ + struct Args; + + impl std::hint::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = $args; + } + + $($module)::+::codeview_annotation::(); + }}; +} + +// At OPT0, the API wrapper function is not inlined +// so we must check it exists and calls the intrinsic +// OPT0-LABEL: ; core::hint::codeview_annotation:: +// OPT0: define internal void [[API_SINGLE_WRAPPER:@[^(]+]]() +// OPT0: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +// OPT0-LABEL: ; core::hint::codeview_annotation:: +// OPT0: define internal void [[API_MULTIPLE_WRAPPER:@[^(]+]]() +// OPT0: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) + +// === Intrinsic tests === +// CHECK-LABEL: @single +// CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) +#[no_mangle] +pub fn single() { + call_intrinsic!(&["single_string"]); +} + +// CHECK-LABEL: @multiple +// CHECK: call void @llvm.codeview.annotation(metadata [[MULTIPLE:![0-9]+]]) +#[no_mangle] +pub fn multiple() { + call_intrinsic!(&["multi1", "multi2", "multi3"]); +} + +const STR_A: &str = "str_a"; +const STR_B: &str = "str_b"; +const STR_C: &str = "str_c"; + +// CHECK-LABEL: @named_const_elements +// CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) +#[no_mangle] +pub fn named_const_elements() { + call_intrinsic!(&[STR_A, STR_B, STR_C]); +} + +// CHECK-LABEL: @mixed_named_consts_and_literals +// CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) +#[no_mangle] +pub fn mixed_named_consts_and_literals() { + call_intrinsic!(&[STR_A, "mixed_literal1", "mixed_literal2"]); +} + +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]+]]) +#[no_mangle] +pub fn named_const_slice() { + call_intrinsic!(STRS_SLICE); +} + +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]+]]) +#[no_mangle] +pub fn named_const_array_ref() { + call_intrinsic!(&STRS_ARRAY); +} + +static STATIC_STRS_ARRAY: [&str; 3] = ["static1", "static2", "static3"]; + +// CHECK-LABEL: @static_array_ref +// CHECK: call void @llvm.codeview.annotation(metadata [[STATIC_ARRAY:![0-9]+]]) +#[no_mangle] +pub fn static_array_ref() { + call_intrinsic!(&STATIC_STRS_ARRAY); +} + +static STATIC_STRING_BYTES: [u8; 5] = *b"bytes"; +static STATIC_STRING: &str = unsafe { core::str::from_utf8_unchecked(&STATIC_STRING_BYTES) }; + +// CHECK-LABEL: @static_string_element +// CHECK: call void @llvm.codeview.annotation(metadata [[STATIC_STRING:![0-9]+]]) +#[no_mangle] +pub fn static_string_element() { + call_intrinsic!(&["string1", STATIC_STRING, "string3"]); +} + +// CHECK-LABEL: @empty_strings +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_STRINGS:![0-9]+]]) +#[no_mangle] +pub fn empty_strings() { + call_intrinsic!(&["", "", "string1"]); +} + +// CHECK-LABEL: @empty_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_SLICE:![0-9]+]]) +#[no_mangle] +pub fn empty_slice() { + call_intrinsic!(&[]); +} + +const EMPTY_STRS_SLICE: &[&str] = &[]; + +// CHECK-LABEL: @named_empty_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_SLICE]]) +#[no_mangle] +pub fn named_empty_slice() { + call_intrinsic!(EMPTY_STRS_SLICE); +} + +// 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() { + call_intrinsic!(&["dup1", "dup2", "dup3"]); + call_intrinsic!(&["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() { + call_intrinsic!(&["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() { + call_intrinsic!(&["func_dup1", "func_dup2", "func_dup3"]); +} + +// === API tests === +// CHECK-LABEL: @api_single_annotation +// OPT0: call void [[API_SINGLE_WRAPPER]]() +// OPT3: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +#[no_mangle] +pub fn api_single_annotation() { + call_api!(&["intr_single_string"]); +} + +// CHECK-LABEL: @api_multiple_annotations +// OPT0: call void [[API_MULTIPLE_WRAPPER]]() +// OPT3: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +#[no_mangle] +pub fn api_multiple_annotations() { + call_api!(&["intr_multi1", "intr_multi2", "intr_multi3"]); +} + +// Metadata definitions are at the end of LLVM IR, so check them here +// CHECK-DAG: [[SINGLE]] = !{!"single_string"} +// CHECK-DAG: [[MULTIPLE]] = !{!"multi1", !"multi2", !"multi3"} +// CHECK-DAG: [[NAMED_CONST]] = !{!"str_a", !"str_b", !"str_c"} +// CHECK-DAG: [[MIXED]] = !{!"str_a", !"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: [[STATIC_ARRAY]] = !{!"static1", !"static2", !"static3"} +// CHECK-DAG: [[STATIC_STRING]] = !{!"string1", !"bytes", !"string3"} +// CHECK-DAG: [[EMPTY_STRINGS]] = !{!"", !"", !"string1"} +// CHECK-DAG: [[EMPTY_SLICE]] = !{} +// CHECK-DAG: [[DUP]] = !{!"dup1", !"dup2", !"dup3"} +// CHECK-DAG: [[FUNC_DUP]] = !{!"func_dup1", !"func_dup2", !"func_dup3"} +// CHECK-DAG: !{{[0-9]+}} = !{!"intr_single_string"} +// CHECK-DAG: !{{[0-9]+}} = !{!"intr_multi1", !"intr_multi2", !"intr_multi3"} 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..5f9519d29b173 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs @@ -0,0 +1,9 @@ +struct Args; + +impl std::hint::CodeViewAnnotationArgs for Args { //~ ERROR use of unstable library feature `codeview_annotation` + const ARGS: &[&str] = &["string1", "string2", "string3"]; //~ ERROR use of unstable library feature `codeview_annotation` +} + +fn main() { + std::hint::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..aeff45b662c86 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -0,0 +1,30 @@ +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:4:5 + | +LL | const ARGS: &[&str] = &["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:3:6 + | +LL | impl std::hint::CodeViewAnnotationArgs for Args { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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:8:5 + | +LL | std::hint::codeview_annotation::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/intrinsics/codeview-annotation-bad-args.rs b/tests/ui/intrinsics/codeview-annotation-bad-args.rs new file mode 100644 index 0000000000000..35f1fbf5a88a3 --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation-bad-args.rs @@ -0,0 +1,44 @@ +// Verifies that `codeview_annotation` emits a compile-time error +// when the `CodeViewAnnotationArgs::ARGS` associated constant +// does not const eval successfully. + +//@ build-fail +//@ only-msvc + +#![feature(codeview_annotation)] + +use std::hint::{CodeViewAnnotationArgs, codeview_annotation}; + +// `CodeViewAnnotationArgs::ARGS` does not const eval successfully +struct Invalid; + +impl CodeViewAnnotationArgs for Invalid { + const ARGS: &[&str] = panic!("panic"); //~ ERROR evaluation panicked: panic [E0080] +} + + +// `CodeViewAnnotationArgs::ARGS` does not const eval successfully +// with generics involved +trait GetName { + const NAME: &str; +} + +impl GetName for i32 { + const NAME: &str = panic!("panic"); //~ ERROR evaluation panicked: panic [E0080] +} + +struct Args(std::marker::PhantomData); + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &[T::NAME]; +} + +fn emit() { + codeview_annotation::>(); +} + + +fn main() { + codeview_annotation::(); + emit::(); +} diff --git a/tests/ui/intrinsics/codeview-annotation-bad-args.stderr b/tests/ui/intrinsics/codeview-annotation-bad-args.stderr new file mode 100644 index 0000000000000..4a48fed7c99b4 --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation-bad-args.stderr @@ -0,0 +1,21 @@ +error[E0080]: evaluation panicked: panic + --> $DIR/codeview-annotation-bad-args.rs:27:24 + | +LL | const NAME: &str = panic!("panic"); + | ^^^^^^^^^^^^^^^ evaluation of `::NAME` failed here + +note: erroneous constant encountered + --> $DIR/codeview-annotation-bad-args.rs:33:29 + | +LL | const ARGS: &[&str] = &[T::NAME]; + | ^^^^^^^ + +error[E0080]: evaluation panicked: panic + --> $DIR/codeview-annotation-bad-args.rs:16:27 + | +LL | const ARGS: &[&str] = panic!("panic"); + | ^^^^^^^^^^^^^^^ evaluation of `::ARGS` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/intrinsics/codeview-annotation.rs b/tests/ui/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..2ca9ecaddd34d --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation.rs @@ -0,0 +1,176 @@ +// Verifies that calls to the `codeview_annotation` API +// and the intrinsic compile successfully + +//@ build-pass + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +// === Helper macros === +macro_rules! call_intrinsic { + ($args:expr) => {{ + call_codeview_annotation!(std::intrinsics, $args); + }}; +} + +macro_rules! call_api { + ($args:expr) => {{ + call_codeview_annotation!(std::hint, $args); + }}; +} + +macro_rules! call_codeview_annotation { + ($($module:ident)::+, $args:expr) => {{ + struct Args; + + impl std::hint::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = $args; + } + + $($module)::+::codeview_annotation::(); + }}; +} + +// === API tests === +fn single() { + call_api!(&["string1"]); +} + +fn multiple() { + call_api!(&["string1", "string2", "string3"]); +} + +const STR_A: &str = "string1"; +const STR_B: &str = "string2"; +const STR_C: &str = "string3"; + +fn named_consts() { + call_api!(&[STR_A, STR_B, STR_C]); +} + +fn mixed_named_consts_and_literals() { + call_api!(&[STR_A, "string2", "string3"]); +} + +const STRS_SLICE: &[&str] = &["string1", "string2", "string3"]; + +fn named_const_slice() { + call_api!(STRS_SLICE); +} + +const STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; + +fn named_const_array_ref() { + call_api!(&STRS_ARRAY); +} + +static STATIC_STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; + +fn static_array_ref() { + call_api!(&STATIC_STRS_ARRAY); +} + +// Data associated with the types of +// some variables passed to codeview_annotation +// e.g. type names +fn consts_associated_with_vars() { + // A trait that lets you assign names to types + trait TypeName { + const NAME: &str; + } + + struct A; + impl TypeName for A { + const NAME: &str = "A"; + } + + struct B; + impl TypeName for B { + const NAME: &str = "B"; + } + + // This function's purpose is to monomorphize and infer + // the types of its arguments and pass their associated + // consts to codeview_annotation + fn emit_annotation(_var1: &T1, _var2: &T2) { + struct Args(std::marker::PhantomData<(T1, T2)>); + + impl std::hint::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &["string", T1::NAME, T2::NAME]; + } + + std::hint::codeview_annotation::>(); + } + + // Strings "A" and "B" get passed to + // codeview_annotation for a and b respectively + let a = A; + let b = B; + emit_annotation(&a, &b); +} + +// An associated const slice passed as args +// to codeview_annotation +trait HasStrs { + const STRS: &[&str]; +} + +impl HasStrs for i32 { + const STRS: &[&str] = &["string1", "string2", "string3"]; +} + +fn generic_associated_const_slice() { + struct Args(std::marker::PhantomData); + + impl std::hint::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = T::STRS; + } + + std::hint::codeview_annotation::>(); +} + +fn empty_strings() { + call_api!(&["", "", "string1"]); +} + +fn empty_slice() { + call_api!(&[]); +} + +const EMPTY_STRS_SLICE: &[&str] = &[]; + +pub fn named_empty_slice() { + call_api!(EMPTY_STRS_SLICE); +} + +// === Intrinsic tests === +fn intrinsic_single() { + call_intrinsic!(&["string1"]); +} + +fn intrinsic_multiple() { + call_intrinsic!(&["string1", "string2", "string3"]); +} + +fn intrinsic_mixed_named_consts_and_literals() { + call_intrinsic!(&[STR_A, "string2", "string3"]); +} + +fn main() { + single(); + multiple(); + named_consts(); + mixed_named_consts_and_literals(); + named_const_slice(); + named_const_array_ref(); + static_array_ref(); + consts_associated_with_vars(); + generic_associated_const_slice::(); + empty_strings(); + empty_slice(); + named_empty_slice(); + + intrinsic_single(); + intrinsic_multiple(); + intrinsic_mixed_named_consts_and_literals(); +}